##// END OF EJS Templates
bundle2: allow compressed bundle...
Pierre-Yves David -
r26404:795f02a2 default
parent child Browse files
Show More
@@ -1,1441 +1,1463 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 import urllib
155 import urllib
156
156
157 from .i18n import _
157 from .i18n import _
158 from . import (
158 from . import (
159 changegroup,
159 changegroup,
160 error,
160 error,
161 obsolete,
161 obsolete,
162 pushkey,
162 pushkey,
163 tags,
163 tags,
164 url,
164 url,
165 util,
165 util,
166 )
166 )
167
167
168 _pack = struct.pack
168 _pack = struct.pack
169 _unpack = struct.unpack
169 _unpack = struct.unpack
170
170
171 _fstreamparamsize = '>i'
171 _fstreamparamsize = '>i'
172 _fpartheadersize = '>i'
172 _fpartheadersize = '>i'
173 _fparttypesize = '>B'
173 _fparttypesize = '>B'
174 _fpartid = '>I'
174 _fpartid = '>I'
175 _fpayloadsize = '>i'
175 _fpayloadsize = '>i'
176 _fpartparamcount = '>BB'
176 _fpartparamcount = '>BB'
177
177
178 preferedchunksize = 4096
178 preferedchunksize = 4096
179
179
180 _parttypeforbidden = re.compile('[^a-zA-Z0-9_:-]')
180 _parttypeforbidden = re.compile('[^a-zA-Z0-9_:-]')
181
181
182 def outdebug(ui, message):
182 def outdebug(ui, message):
183 """debug regarding output stream (bundling)"""
183 """debug regarding output stream (bundling)"""
184 if ui.configbool('devel', 'bundle2.debug', False):
184 if ui.configbool('devel', 'bundle2.debug', False):
185 ui.debug('bundle2-output: %s\n' % message)
185 ui.debug('bundle2-output: %s\n' % message)
186
186
187 def indebug(ui, message):
187 def indebug(ui, message):
188 """debug on input stream (unbundling)"""
188 """debug on input stream (unbundling)"""
189 if ui.configbool('devel', 'bundle2.debug', False):
189 if ui.configbool('devel', 'bundle2.debug', False):
190 ui.debug('bundle2-input: %s\n' % message)
190 ui.debug('bundle2-input: %s\n' % message)
191
191
192 def validateparttype(parttype):
192 def validateparttype(parttype):
193 """raise ValueError if a parttype contains invalid character"""
193 """raise ValueError if a parttype contains invalid character"""
194 if _parttypeforbidden.search(parttype):
194 if _parttypeforbidden.search(parttype):
195 raise ValueError(parttype)
195 raise ValueError(parttype)
196
196
197 def _makefpartparamsizes(nbparams):
197 def _makefpartparamsizes(nbparams):
198 """return a struct format to read part parameter sizes
198 """return a struct format to read part parameter sizes
199
199
200 The number parameters is variable so we need to build that format
200 The number parameters is variable so we need to build that format
201 dynamically.
201 dynamically.
202 """
202 """
203 return '>'+('BB'*nbparams)
203 return '>'+('BB'*nbparams)
204
204
205 parthandlermapping = {}
205 parthandlermapping = {}
206
206
207 def parthandler(parttype, params=()):
207 def parthandler(parttype, params=()):
208 """decorator that register a function as a bundle2 part handler
208 """decorator that register a function as a bundle2 part handler
209
209
210 eg::
210 eg::
211
211
212 @parthandler('myparttype', ('mandatory', 'param', 'handled'))
212 @parthandler('myparttype', ('mandatory', 'param', 'handled'))
213 def myparttypehandler(...):
213 def myparttypehandler(...):
214 '''process a part of type "my part".'''
214 '''process a part of type "my part".'''
215 ...
215 ...
216 """
216 """
217 validateparttype(parttype)
217 validateparttype(parttype)
218 def _decorator(func):
218 def _decorator(func):
219 lparttype = parttype.lower() # enforce lower case matching.
219 lparttype = parttype.lower() # enforce lower case matching.
220 assert lparttype not in parthandlermapping
220 assert lparttype not in parthandlermapping
221 parthandlermapping[lparttype] = func
221 parthandlermapping[lparttype] = func
222 func.params = frozenset(params)
222 func.params = frozenset(params)
223 return func
223 return func
224 return _decorator
224 return _decorator
225
225
226 class unbundlerecords(object):
226 class unbundlerecords(object):
227 """keep record of what happens during and unbundle
227 """keep record of what happens during and unbundle
228
228
229 New records are added using `records.add('cat', obj)`. Where 'cat' is a
229 New records are added using `records.add('cat', obj)`. Where 'cat' is a
230 category of record and obj is an arbitrary object.
230 category of record and obj is an arbitrary object.
231
231
232 `records['cat']` will return all entries of this category 'cat'.
232 `records['cat']` will return all entries of this category 'cat'.
233
233
234 Iterating on the object itself will yield `('category', obj)` tuples
234 Iterating on the object itself will yield `('category', obj)` tuples
235 for all entries.
235 for all entries.
236
236
237 All iterations happens in chronological order.
237 All iterations happens in chronological order.
238 """
238 """
239
239
240 def __init__(self):
240 def __init__(self):
241 self._categories = {}
241 self._categories = {}
242 self._sequences = []
242 self._sequences = []
243 self._replies = {}
243 self._replies = {}
244
244
245 def add(self, category, entry, inreplyto=None):
245 def add(self, category, entry, inreplyto=None):
246 """add a new record of a given category.
246 """add a new record of a given category.
247
247
248 The entry can then be retrieved in the list returned by
248 The entry can then be retrieved in the list returned by
249 self['category']."""
249 self['category']."""
250 self._categories.setdefault(category, []).append(entry)
250 self._categories.setdefault(category, []).append(entry)
251 self._sequences.append((category, entry))
251 self._sequences.append((category, entry))
252 if inreplyto is not None:
252 if inreplyto is not None:
253 self.getreplies(inreplyto).add(category, entry)
253 self.getreplies(inreplyto).add(category, entry)
254
254
255 def getreplies(self, partid):
255 def getreplies(self, partid):
256 """get the records that are replies to a specific part"""
256 """get the records that are replies to a specific part"""
257 return self._replies.setdefault(partid, unbundlerecords())
257 return self._replies.setdefault(partid, unbundlerecords())
258
258
259 def __getitem__(self, cat):
259 def __getitem__(self, cat):
260 return tuple(self._categories.get(cat, ()))
260 return tuple(self._categories.get(cat, ()))
261
261
262 def __iter__(self):
262 def __iter__(self):
263 return iter(self._sequences)
263 return iter(self._sequences)
264
264
265 def __len__(self):
265 def __len__(self):
266 return len(self._sequences)
266 return len(self._sequences)
267
267
268 def __nonzero__(self):
268 def __nonzero__(self):
269 return bool(self._sequences)
269 return bool(self._sequences)
270
270
271 class bundleoperation(object):
271 class bundleoperation(object):
272 """an object that represents a single bundling process
272 """an object that represents a single bundling process
273
273
274 Its purpose is to carry unbundle-related objects and states.
274 Its purpose is to carry unbundle-related objects and states.
275
275
276 A new object should be created at the beginning of each bundle processing.
276 A new object should be created at the beginning of each bundle processing.
277 The object is to be returned by the processing function.
277 The object is to be returned by the processing function.
278
278
279 The object has very little content now it will ultimately contain:
279 The object has very little content now it will ultimately contain:
280 * an access to the repo the bundle is applied to,
280 * an access to the repo the bundle is applied to,
281 * a ui object,
281 * a ui object,
282 * a way to retrieve a transaction to add changes to the repo,
282 * a way to retrieve a transaction to add changes to the repo,
283 * a way to record the result of processing each part,
283 * a way to record the result of processing each part,
284 * a way to construct a bundle response when applicable.
284 * a way to construct a bundle response when applicable.
285 """
285 """
286
286
287 def __init__(self, repo, transactiongetter, captureoutput=True):
287 def __init__(self, repo, transactiongetter, captureoutput=True):
288 self.repo = repo
288 self.repo = repo
289 self.ui = repo.ui
289 self.ui = repo.ui
290 self.records = unbundlerecords()
290 self.records = unbundlerecords()
291 self.gettransaction = transactiongetter
291 self.gettransaction = transactiongetter
292 self.reply = None
292 self.reply = None
293 self.captureoutput = captureoutput
293 self.captureoutput = captureoutput
294
294
295 class TransactionUnavailable(RuntimeError):
295 class TransactionUnavailable(RuntimeError):
296 pass
296 pass
297
297
298 def _notransaction():
298 def _notransaction():
299 """default method to get a transaction while processing a bundle
299 """default method to get a transaction while processing a bundle
300
300
301 Raise an exception to highlight the fact that no transaction was expected
301 Raise an exception to highlight the fact that no transaction was expected
302 to be created"""
302 to be created"""
303 raise TransactionUnavailable()
303 raise TransactionUnavailable()
304
304
305 def processbundle(repo, unbundler, transactiongetter=None, op=None):
305 def processbundle(repo, unbundler, transactiongetter=None, op=None):
306 """This function process a bundle, apply effect to/from a repo
306 """This function process a bundle, apply effect to/from a repo
307
307
308 It iterates over each part then searches for and uses the proper handling
308 It iterates over each part then searches for and uses the proper handling
309 code to process the part. Parts are processed in order.
309 code to process the part. Parts are processed in order.
310
310
311 This is very early version of this function that will be strongly reworked
311 This is very early version of this function that will be strongly reworked
312 before final usage.
312 before final usage.
313
313
314 Unknown Mandatory part will abort the process.
314 Unknown Mandatory part will abort the process.
315
315
316 It is temporarily possible to provide a prebuilt bundleoperation to the
316 It is temporarily possible to provide a prebuilt bundleoperation to the
317 function. This is used to ensure output is properly propagated in case of
317 function. This is used to ensure output is properly propagated in case of
318 an error during the unbundling. This output capturing part will likely be
318 an error during the unbundling. This output capturing part will likely be
319 reworked and this ability will probably go away in the process.
319 reworked and this ability will probably go away in the process.
320 """
320 """
321 if op is None:
321 if op is None:
322 if transactiongetter is None:
322 if transactiongetter is None:
323 transactiongetter = _notransaction
323 transactiongetter = _notransaction
324 op = bundleoperation(repo, transactiongetter)
324 op = bundleoperation(repo, transactiongetter)
325 # todo:
325 # todo:
326 # - replace this is a init function soon.
326 # - replace this is a init function soon.
327 # - exception catching
327 # - exception catching
328 unbundler.params
328 unbundler.params
329 if repo.ui.debugflag:
329 if repo.ui.debugflag:
330 msg = ['bundle2-input-bundle:']
330 msg = ['bundle2-input-bundle:']
331 if unbundler.params:
331 if unbundler.params:
332 msg.append(' %i params')
332 msg.append(' %i params')
333 if op.gettransaction is None:
333 if op.gettransaction is None:
334 msg.append(' no-transaction')
334 msg.append(' no-transaction')
335 else:
335 else:
336 msg.append(' with-transaction')
336 msg.append(' with-transaction')
337 msg.append('\n')
337 msg.append('\n')
338 repo.ui.debug(''.join(msg))
338 repo.ui.debug(''.join(msg))
339 iterparts = enumerate(unbundler.iterparts())
339 iterparts = enumerate(unbundler.iterparts())
340 part = None
340 part = None
341 nbpart = 0
341 nbpart = 0
342 try:
342 try:
343 for nbpart, part in iterparts:
343 for nbpart, part in iterparts:
344 _processpart(op, part)
344 _processpart(op, part)
345 except BaseException as exc:
345 except BaseException as exc:
346 for nbpart, part in iterparts:
346 for nbpart, part in iterparts:
347 # consume the bundle content
347 # consume the bundle content
348 part.seek(0, 2)
348 part.seek(0, 2)
349 # Small hack to let caller code distinguish exceptions from bundle2
349 # Small hack to let caller code distinguish exceptions from bundle2
350 # processing from processing the old format. This is mostly
350 # processing from processing the old format. This is mostly
351 # needed to handle different return codes to unbundle according to the
351 # needed to handle different return codes to unbundle according to the
352 # type of bundle. We should probably clean up or drop this return code
352 # type of bundle. We should probably clean up or drop this return code
353 # craziness in a future version.
353 # craziness in a future version.
354 exc.duringunbundle2 = True
354 exc.duringunbundle2 = True
355 salvaged = []
355 salvaged = []
356 replycaps = None
356 replycaps = None
357 if op.reply is not None:
357 if op.reply is not None:
358 salvaged = op.reply.salvageoutput()
358 salvaged = op.reply.salvageoutput()
359 replycaps = op.reply.capabilities
359 replycaps = op.reply.capabilities
360 exc._replycaps = replycaps
360 exc._replycaps = replycaps
361 exc._bundle2salvagedoutput = salvaged
361 exc._bundle2salvagedoutput = salvaged
362 raise
362 raise
363 finally:
363 finally:
364 repo.ui.debug('bundle2-input-bundle: %i parts total\n' % nbpart)
364 repo.ui.debug('bundle2-input-bundle: %i parts total\n' % nbpart)
365
365
366 return op
366 return op
367
367
368 def _processpart(op, part):
368 def _processpart(op, part):
369 """process a single part from a bundle
369 """process a single part from a bundle
370
370
371 The part is guaranteed to have been fully consumed when the function exits
371 The part is guaranteed to have been fully consumed when the function exits
372 (even if an exception is raised)."""
372 (even if an exception is raised)."""
373 status = 'unknown' # used by debug output
373 status = 'unknown' # used by debug output
374 try:
374 try:
375 try:
375 try:
376 handler = parthandlermapping.get(part.type)
376 handler = parthandlermapping.get(part.type)
377 if handler is None:
377 if handler is None:
378 status = 'unsupported-type'
378 status = 'unsupported-type'
379 raise error.BundleUnknownFeatureError(parttype=part.type)
379 raise error.BundleUnknownFeatureError(parttype=part.type)
380 indebug(op.ui, 'found a handler for part %r' % part.type)
380 indebug(op.ui, 'found a handler for part %r' % part.type)
381 unknownparams = part.mandatorykeys - handler.params
381 unknownparams = part.mandatorykeys - handler.params
382 if unknownparams:
382 if unknownparams:
383 unknownparams = list(unknownparams)
383 unknownparams = list(unknownparams)
384 unknownparams.sort()
384 unknownparams.sort()
385 status = 'unsupported-params (%s)' % unknownparams
385 status = 'unsupported-params (%s)' % unknownparams
386 raise error.BundleUnknownFeatureError(parttype=part.type,
386 raise error.BundleUnknownFeatureError(parttype=part.type,
387 params=unknownparams)
387 params=unknownparams)
388 status = 'supported'
388 status = 'supported'
389 except error.BundleUnknownFeatureError as exc:
389 except error.BundleUnknownFeatureError as exc:
390 if part.mandatory: # mandatory parts
390 if part.mandatory: # mandatory parts
391 raise
391 raise
392 indebug(op.ui, 'ignoring unsupported advisory part %s' % exc)
392 indebug(op.ui, 'ignoring unsupported advisory part %s' % exc)
393 return # skip to part processing
393 return # skip to part processing
394 finally:
394 finally:
395 if op.ui.debugflag:
395 if op.ui.debugflag:
396 msg = ['bundle2-input-part: "%s"' % part.type]
396 msg = ['bundle2-input-part: "%s"' % part.type]
397 if not part.mandatory:
397 if not part.mandatory:
398 msg.append(' (advisory)')
398 msg.append(' (advisory)')
399 nbmp = len(part.mandatorykeys)
399 nbmp = len(part.mandatorykeys)
400 nbap = len(part.params) - nbmp
400 nbap = len(part.params) - nbmp
401 if nbmp or nbap:
401 if nbmp or nbap:
402 msg.append(' (params:')
402 msg.append(' (params:')
403 if nbmp:
403 if nbmp:
404 msg.append(' %i mandatory' % nbmp)
404 msg.append(' %i mandatory' % nbmp)
405 if nbap:
405 if nbap:
406 msg.append(' %i advisory' % nbmp)
406 msg.append(' %i advisory' % nbmp)
407 msg.append(')')
407 msg.append(')')
408 msg.append(' %s\n' % status)
408 msg.append(' %s\n' % status)
409 op.ui.debug(''.join(msg))
409 op.ui.debug(''.join(msg))
410
410
411 # handler is called outside the above try block so that we don't
411 # handler is called outside the above try block so that we don't
412 # risk catching KeyErrors from anything other than the
412 # risk catching KeyErrors from anything other than the
413 # parthandlermapping lookup (any KeyError raised by handler()
413 # parthandlermapping lookup (any KeyError raised by handler()
414 # itself represents a defect of a different variety).
414 # itself represents a defect of a different variety).
415 output = None
415 output = None
416 if op.captureoutput and op.reply is not None:
416 if op.captureoutput and op.reply is not None:
417 op.ui.pushbuffer(error=True, subproc=True)
417 op.ui.pushbuffer(error=True, subproc=True)
418 output = ''
418 output = ''
419 try:
419 try:
420 handler(op, part)
420 handler(op, part)
421 finally:
421 finally:
422 if output is not None:
422 if output is not None:
423 output = op.ui.popbuffer()
423 output = op.ui.popbuffer()
424 if output:
424 if output:
425 outpart = op.reply.newpart('output', data=output,
425 outpart = op.reply.newpart('output', data=output,
426 mandatory=False)
426 mandatory=False)
427 outpart.addparam('in-reply-to', str(part.id), mandatory=False)
427 outpart.addparam('in-reply-to', str(part.id), mandatory=False)
428 finally:
428 finally:
429 # consume the part content to not corrupt the stream.
429 # consume the part content to not corrupt the stream.
430 part.seek(0, 2)
430 part.seek(0, 2)
431
431
432
432
433 def decodecaps(blob):
433 def decodecaps(blob):
434 """decode a bundle2 caps bytes blob into a dictionary
434 """decode a bundle2 caps bytes blob into a dictionary
435
435
436 The blob is a list of capabilities (one per line)
436 The blob is a list of capabilities (one per line)
437 Capabilities may have values using a line of the form::
437 Capabilities may have values using a line of the form::
438
438
439 capability=value1,value2,value3
439 capability=value1,value2,value3
440
440
441 The values are always a list."""
441 The values are always a list."""
442 caps = {}
442 caps = {}
443 for line in blob.splitlines():
443 for line in blob.splitlines():
444 if not line:
444 if not line:
445 continue
445 continue
446 if '=' not in line:
446 if '=' not in line:
447 key, vals = line, ()
447 key, vals = line, ()
448 else:
448 else:
449 key, vals = line.split('=', 1)
449 key, vals = line.split('=', 1)
450 vals = vals.split(',')
450 vals = vals.split(',')
451 key = urllib.unquote(key)
451 key = urllib.unquote(key)
452 vals = [urllib.unquote(v) for v in vals]
452 vals = [urllib.unquote(v) for v in vals]
453 caps[key] = vals
453 caps[key] = vals
454 return caps
454 return caps
455
455
456 def encodecaps(caps):
456 def encodecaps(caps):
457 """encode a bundle2 caps dictionary into a bytes blob"""
457 """encode a bundle2 caps dictionary into a bytes blob"""
458 chunks = []
458 chunks = []
459 for ca in sorted(caps):
459 for ca in sorted(caps):
460 vals = caps[ca]
460 vals = caps[ca]
461 ca = urllib.quote(ca)
461 ca = urllib.quote(ca)
462 vals = [urllib.quote(v) for v in vals]
462 vals = [urllib.quote(v) for v in vals]
463 if vals:
463 if vals:
464 ca = "%s=%s" % (ca, ','.join(vals))
464 ca = "%s=%s" % (ca, ','.join(vals))
465 chunks.append(ca)
465 chunks.append(ca)
466 return '\n'.join(chunks)
466 return '\n'.join(chunks)
467
467
468 class bundle20(object):
468 class bundle20(object):
469 """represent an outgoing bundle2 container
469 """represent an outgoing bundle2 container
470
470
471 Use the `addparam` method to add stream level parameter. and `newpart` to
471 Use the `addparam` method to add stream level parameter. and `newpart` to
472 populate it. Then call `getchunks` to retrieve all the binary chunks of
472 populate it. Then call `getchunks` to retrieve all the binary chunks of
473 data that compose the bundle2 container."""
473 data that compose the bundle2 container."""
474
474
475 _magicstring = 'HG20'
475 _magicstring = 'HG20'
476
476
477 def __init__(self, ui, capabilities=()):
477 def __init__(self, ui, capabilities=()):
478 self.ui = ui
478 self.ui = ui
479 self._params = []
479 self._params = []
480 self._parts = []
480 self._parts = []
481 self.capabilities = dict(capabilities)
481 self.capabilities = dict(capabilities)
482 self._compressor = util.compressors[None]()
483
484 def setcompression(self, alg):
485 """setup core part compression to <alg>"""
486 if alg is None:
487 return
488 assert not any(n.lower() == 'Compression' for n, v in self._params)
489 self.addparam('Compression', alg)
490 self._compressor = util.compressors[alg]()
482
491
483 @property
492 @property
484 def nbparts(self):
493 def nbparts(self):
485 """total number of parts added to the bundler"""
494 """total number of parts added to the bundler"""
486 return len(self._parts)
495 return len(self._parts)
487
496
488 # methods used to defines the bundle2 content
497 # methods used to defines the bundle2 content
489 def addparam(self, name, value=None):
498 def addparam(self, name, value=None):
490 """add a stream level parameter"""
499 """add a stream level parameter"""
491 if not name:
500 if not name:
492 raise ValueError('empty parameter name')
501 raise ValueError('empty parameter name')
493 if name[0] not in string.letters:
502 if name[0] not in string.letters:
494 raise ValueError('non letter first character: %r' % name)
503 raise ValueError('non letter first character: %r' % name)
495 self._params.append((name, value))
504 self._params.append((name, value))
496
505
497 def addpart(self, part):
506 def addpart(self, part):
498 """add a new part to the bundle2 container
507 """add a new part to the bundle2 container
499
508
500 Parts contains the actual applicative payload."""
509 Parts contains the actual applicative payload."""
501 assert part.id is None
510 assert part.id is None
502 part.id = len(self._parts) # very cheap counter
511 part.id = len(self._parts) # very cheap counter
503 self._parts.append(part)
512 self._parts.append(part)
504
513
505 def newpart(self, typeid, *args, **kwargs):
514 def newpart(self, typeid, *args, **kwargs):
506 """create a new part and add it to the containers
515 """create a new part and add it to the containers
507
516
508 As the part is directly added to the containers. For now, this means
517 As the part is directly added to the containers. For now, this means
509 that any failure to properly initialize the part after calling
518 that any failure to properly initialize the part after calling
510 ``newpart`` should result in a failure of the whole bundling process.
519 ``newpart`` should result in a failure of the whole bundling process.
511
520
512 You can still fall back to manually create and add if you need better
521 You can still fall back to manually create and add if you need better
513 control."""
522 control."""
514 part = bundlepart(typeid, *args, **kwargs)
523 part = bundlepart(typeid, *args, **kwargs)
515 self.addpart(part)
524 self.addpart(part)
516 return part
525 return part
517
526
518 # methods used to generate the bundle2 stream
527 # methods used to generate the bundle2 stream
519 def getchunks(self):
528 def getchunks(self):
520 if self.ui.debugflag:
529 if self.ui.debugflag:
521 msg = ['bundle2-output-bundle: "%s",' % self._magicstring]
530 msg = ['bundle2-output-bundle: "%s",' % self._magicstring]
522 if self._params:
531 if self._params:
523 msg.append(' (%i params)' % len(self._params))
532 msg.append(' (%i params)' % len(self._params))
524 msg.append(' %i parts total\n' % len(self._parts))
533 msg.append(' %i parts total\n' % len(self._parts))
525 self.ui.debug(''.join(msg))
534 self.ui.debug(''.join(msg))
526 outdebug(self.ui, 'start emission of %s stream' % self._magicstring)
535 outdebug(self.ui, 'start emission of %s stream' % self._magicstring)
527 yield self._magicstring
536 yield self._magicstring
528 param = self._paramchunk()
537 param = self._paramchunk()
529 outdebug(self.ui, 'bundle parameter: %s' % param)
538 outdebug(self.ui, 'bundle parameter: %s' % param)
530 yield _pack(_fstreamparamsize, len(param))
539 yield _pack(_fstreamparamsize, len(param))
531 if param:
540 if param:
532 yield param
541 yield param
542 # starting compression
533 for chunk in self._getcorechunk():
543 for chunk in self._getcorechunk():
534 yield chunk
544 yield self._compressor.compress(chunk)
545 yield self._compressor.flush()
535
546
536 def _paramchunk(self):
547 def _paramchunk(self):
537 """return a encoded version of all stream parameters"""
548 """return a encoded version of all stream parameters"""
538 blocks = []
549 blocks = []
539 for par, value in self._params:
550 for par, value in self._params:
540 par = urllib.quote(par)
551 par = urllib.quote(par)
541 if value is not None:
552 if value is not None:
542 value = urllib.quote(value)
553 value = urllib.quote(value)
543 par = '%s=%s' % (par, value)
554 par = '%s=%s' % (par, value)
544 blocks.append(par)
555 blocks.append(par)
545 return ' '.join(blocks)
556 return ' '.join(blocks)
546
557
547 def _getcorechunk(self):
558 def _getcorechunk(self):
548 """yield chunk for the core part of the bundle
559 """yield chunk for the core part of the bundle
549
560
550 (all but headers and parameters)"""
561 (all but headers and parameters)"""
551 outdebug(self.ui, 'start of parts')
562 outdebug(self.ui, 'start of parts')
552 for part in self._parts:
563 for part in self._parts:
553 outdebug(self.ui, 'bundle part: "%s"' % part.type)
564 outdebug(self.ui, 'bundle part: "%s"' % part.type)
554 for chunk in part.getchunks(ui=self.ui):
565 for chunk in part.getchunks(ui=self.ui):
555 yield chunk
566 yield chunk
556 outdebug(self.ui, 'end of bundle')
567 outdebug(self.ui, 'end of bundle')
557 yield _pack(_fpartheadersize, 0)
568 yield _pack(_fpartheadersize, 0)
558
569
559
570
560 def salvageoutput(self):
571 def salvageoutput(self):
561 """return a list with a copy of all output parts in the bundle
572 """return a list with a copy of all output parts in the bundle
562
573
563 This is meant to be used during error handling to make sure we preserve
574 This is meant to be used during error handling to make sure we preserve
564 server output"""
575 server output"""
565 salvaged = []
576 salvaged = []
566 for part in self._parts:
577 for part in self._parts:
567 if part.type.startswith('output'):
578 if part.type.startswith('output'):
568 salvaged.append(part.copy())
579 salvaged.append(part.copy())
569 return salvaged
580 return salvaged
570
581
571
582
572 class unpackermixin(object):
583 class unpackermixin(object):
573 """A mixin to extract bytes and struct data from a stream"""
584 """A mixin to extract bytes and struct data from a stream"""
574
585
575 def __init__(self, fp):
586 def __init__(self, fp):
576 self._fp = fp
587 self._fp = fp
577 self._seekable = (util.safehasattr(fp, 'seek') and
588 self._seekable = (util.safehasattr(fp, 'seek') and
578 util.safehasattr(fp, 'tell'))
589 util.safehasattr(fp, 'tell'))
579
590
580 def _unpack(self, format):
591 def _unpack(self, format):
581 """unpack this struct format from the stream"""
592 """unpack this struct format from the stream"""
582 data = self._readexact(struct.calcsize(format))
593 data = self._readexact(struct.calcsize(format))
583 return _unpack(format, data)
594 return _unpack(format, data)
584
595
585 def _readexact(self, size):
596 def _readexact(self, size):
586 """read exactly <size> bytes from the stream"""
597 """read exactly <size> bytes from the stream"""
587 return changegroup.readexactly(self._fp, size)
598 return changegroup.readexactly(self._fp, size)
588
599
589 def seek(self, offset, whence=0):
600 def seek(self, offset, whence=0):
590 """move the underlying file pointer"""
601 """move the underlying file pointer"""
591 if self._seekable:
602 if self._seekable:
592 return self._fp.seek(offset, whence)
603 return self._fp.seek(offset, whence)
593 else:
604 else:
594 raise NotImplementedError(_('File pointer is not seekable'))
605 raise NotImplementedError(_('File pointer is not seekable'))
595
606
596 def tell(self):
607 def tell(self):
597 """return the file offset, or None if file is not seekable"""
608 """return the file offset, or None if file is not seekable"""
598 if self._seekable:
609 if self._seekable:
599 try:
610 try:
600 return self._fp.tell()
611 return self._fp.tell()
601 except IOError as e:
612 except IOError as e:
602 if e.errno == errno.ESPIPE:
613 if e.errno == errno.ESPIPE:
603 self._seekable = False
614 self._seekable = False
604 else:
615 else:
605 raise
616 raise
606 return None
617 return None
607
618
608 def close(self):
619 def close(self):
609 """close underlying file"""
620 """close underlying file"""
610 if util.safehasattr(self._fp, 'close'):
621 if util.safehasattr(self._fp, 'close'):
611 return self._fp.close()
622 return self._fp.close()
612
623
613 def getunbundler(ui, fp, magicstring=None):
624 def getunbundler(ui, fp, magicstring=None):
614 """return a valid unbundler object for a given magicstring"""
625 """return a valid unbundler object for a given magicstring"""
615 if magicstring is None:
626 if magicstring is None:
616 magicstring = changegroup.readexactly(fp, 4)
627 magicstring = changegroup.readexactly(fp, 4)
617 magic, version = magicstring[0:2], magicstring[2:4]
628 magic, version = magicstring[0:2], magicstring[2:4]
618 if magic != 'HG':
629 if magic != 'HG':
619 raise util.Abort(_('not a Mercurial bundle'))
630 raise util.Abort(_('not a Mercurial bundle'))
620 unbundlerclass = formatmap.get(version)
631 unbundlerclass = formatmap.get(version)
621 if unbundlerclass is None:
632 if unbundlerclass is None:
622 raise util.Abort(_('unknown bundle version %s') % version)
633 raise util.Abort(_('unknown bundle version %s') % version)
623 unbundler = unbundlerclass(ui, fp)
634 unbundler = unbundlerclass(ui, fp)
624 indebug(ui, 'start processing of %s stream' % magicstring)
635 indebug(ui, 'start processing of %s stream' % magicstring)
625 return unbundler
636 return unbundler
626
637
627 class unbundle20(unpackermixin):
638 class unbundle20(unpackermixin):
628 """interpret a bundle2 stream
639 """interpret a bundle2 stream
629
640
630 This class is fed with a binary stream and yields parts through its
641 This class is fed with a binary stream and yields parts through its
631 `iterparts` methods."""
642 `iterparts` methods."""
632
643
633 def __init__(self, ui, fp):
644 def __init__(self, ui, fp):
634 """If header is specified, we do not read it out of the stream."""
645 """If header is specified, we do not read it out of the stream."""
635 self.ui = ui
646 self.ui = ui
647 self._decompressor = util.decompressors[None]
636 super(unbundle20, self).__init__(fp)
648 super(unbundle20, self).__init__(fp)
637
649
638 @util.propertycache
650 @util.propertycache
639 def params(self):
651 def params(self):
640 """dictionary of stream level parameters"""
652 """dictionary of stream level parameters"""
641 indebug(self.ui, 'reading bundle2 stream parameters')
653 indebug(self.ui, 'reading bundle2 stream parameters')
642 params = {}
654 params = {}
643 paramssize = self._unpack(_fstreamparamsize)[0]
655 paramssize = self._unpack(_fstreamparamsize)[0]
644 if paramssize < 0:
656 if paramssize < 0:
645 raise error.BundleValueError('negative bundle param size: %i'
657 raise error.BundleValueError('negative bundle param size: %i'
646 % paramssize)
658 % paramssize)
647 if paramssize:
659 if paramssize:
648 for p in self._readexact(paramssize).split(' '):
660 for p in self._readexact(paramssize).split(' '):
649 p = p.split('=', 1)
661 p = p.split('=', 1)
650 p = [urllib.unquote(i) for i in p]
662 p = [urllib.unquote(i) for i in p]
651 if len(p) < 2:
663 if len(p) < 2:
652 p.append(None)
664 p.append(None)
653 self._processparam(*p)
665 self._processparam(*p)
654 params[p[0]] = p[1]
666 params[p[0]] = p[1]
655 return params
667 return params
656
668
657 def _processparam(self, name, value):
669 def _processparam(self, name, value):
658 """process a parameter, applying its effect if needed
670 """process a parameter, applying its effect if needed
659
671
660 Parameter starting with a lower case letter are advisory and will be
672 Parameter starting with a lower case letter are advisory and will be
661 ignored when unknown. Those starting with an upper case letter are
673 ignored when unknown. Those starting with an upper case letter are
662 mandatory and will this function will raise a KeyError when unknown.
674 mandatory and will this function will raise a KeyError when unknown.
663
675
664 Note: no option are currently supported. Any input will be either
676 Note: no option are currently supported. Any input will be either
665 ignored or failing.
677 ignored or failing.
666 """
678 """
667 if not name:
679 if not name:
668 raise ValueError('empty parameter name')
680 raise ValueError('empty parameter name')
669 if name[0] not in string.letters:
681 if name[0] not in string.letters:
670 raise ValueError('non letter first character: %r' % name)
682 raise ValueError('non letter first character: %r' % name)
671 try:
683 try:
672 handler = b2streamparamsmap[name.lower()]
684 handler = b2streamparamsmap[name.lower()]
673 except KeyError:
685 except KeyError:
674 if name[0].islower():
686 if name[0].islower():
675 indebug(self.ui, "ignoring unknown parameter %r" % name)
687 indebug(self.ui, "ignoring unknown parameter %r" % name)
676 else:
688 else:
677 raise error.BundleUnknownFeatureError(params=(name,))
689 raise error.BundleUnknownFeatureError(params=(name,))
678 else:
690 else:
679 handler(self, name, value)
691 handler(self, name, value)
680
692
681 def iterparts(self):
693 def iterparts(self):
682 """yield all parts contained in the stream"""
694 """yield all parts contained in the stream"""
683 # make sure param have been loaded
695 # make sure param have been loaded
684 self.params
696 self.params
697 # From there, payload need to be decompressed
698 self._fp = self._decompressor(self._fp)
685 indebug(self.ui, 'start extraction of bundle2 parts')
699 indebug(self.ui, 'start extraction of bundle2 parts')
686 headerblock = self._readpartheader()
700 headerblock = self._readpartheader()
687 while headerblock is not None:
701 while headerblock is not None:
688 part = unbundlepart(self.ui, headerblock, self._fp)
702 part = unbundlepart(self.ui, headerblock, self._fp)
689 yield part
703 yield part
690 part.seek(0, 2)
704 part.seek(0, 2)
691 headerblock = self._readpartheader()
705 headerblock = self._readpartheader()
692 indebug(self.ui, 'end of bundle2 stream')
706 indebug(self.ui, 'end of bundle2 stream')
693
707
694 def _readpartheader(self):
708 def _readpartheader(self):
695 """reads a part header size and return the bytes blob
709 """reads a part header size and return the bytes blob
696
710
697 returns None if empty"""
711 returns None if empty"""
698 headersize = self._unpack(_fpartheadersize)[0]
712 headersize = self._unpack(_fpartheadersize)[0]
699 if headersize < 0:
713 if headersize < 0:
700 raise error.BundleValueError('negative part header size: %i'
714 raise error.BundleValueError('negative part header size: %i'
701 % headersize)
715 % headersize)
702 indebug(self.ui, 'part header size: %i' % headersize)
716 indebug(self.ui, 'part header size: %i' % headersize)
703 if headersize:
717 if headersize:
704 return self._readexact(headersize)
718 return self._readexact(headersize)
705 return None
719 return None
706
720
707 def compressed(self):
721 def compressed(self):
708 return False
722 return False
709
723
710 formatmap = {'20': unbundle20}
724 formatmap = {'20': unbundle20}
711
725
712 b2streamparamsmap = {}
726 b2streamparamsmap = {}
713
727
714 def b2streamparamhandler(name):
728 def b2streamparamhandler(name):
715 """register a handler for a stream level parameter"""
729 """register a handler for a stream level parameter"""
716 def decorator(func):
730 def decorator(func):
717 assert name not in formatmap
731 assert name not in formatmap
718 b2streamparamsmap[name] = func
732 b2streamparamsmap[name] = func
719 return func
733 return func
720 return decorator
734 return decorator
721
735
736 @b2streamparamhandler('compression')
737 def processcompression(unbundler, param, value):
738 """read compression parameter and install payload decompression"""
739 if value not in util.decompressors:
740 raise error.BundleUnknownFeatureError(params=(param,),
741 values=(value,))
742 unbundler._decompressor = util.decompressors[value]
743
722 class bundlepart(object):
744 class bundlepart(object):
723 """A bundle2 part contains application level payload
745 """A bundle2 part contains application level payload
724
746
725 The part `type` is used to route the part to the application level
747 The part `type` is used to route the part to the application level
726 handler.
748 handler.
727
749
728 The part payload is contained in ``part.data``. It could be raw bytes or a
750 The part payload is contained in ``part.data``. It could be raw bytes or a
729 generator of byte chunks.
751 generator of byte chunks.
730
752
731 You can add parameters to the part using the ``addparam`` method.
753 You can add parameters to the part using the ``addparam`` method.
732 Parameters can be either mandatory (default) or advisory. Remote side
754 Parameters can be either mandatory (default) or advisory. Remote side
733 should be able to safely ignore the advisory ones.
755 should be able to safely ignore the advisory ones.
734
756
735 Both data and parameters cannot be modified after the generation has begun.
757 Both data and parameters cannot be modified after the generation has begun.
736 """
758 """
737
759
738 def __init__(self, parttype, mandatoryparams=(), advisoryparams=(),
760 def __init__(self, parttype, mandatoryparams=(), advisoryparams=(),
739 data='', mandatory=True):
761 data='', mandatory=True):
740 validateparttype(parttype)
762 validateparttype(parttype)
741 self.id = None
763 self.id = None
742 self.type = parttype
764 self.type = parttype
743 self._data = data
765 self._data = data
744 self._mandatoryparams = list(mandatoryparams)
766 self._mandatoryparams = list(mandatoryparams)
745 self._advisoryparams = list(advisoryparams)
767 self._advisoryparams = list(advisoryparams)
746 # checking for duplicated entries
768 # checking for duplicated entries
747 self._seenparams = set()
769 self._seenparams = set()
748 for pname, __ in self._mandatoryparams + self._advisoryparams:
770 for pname, __ in self._mandatoryparams + self._advisoryparams:
749 if pname in self._seenparams:
771 if pname in self._seenparams:
750 raise RuntimeError('duplicated params: %s' % pname)
772 raise RuntimeError('duplicated params: %s' % pname)
751 self._seenparams.add(pname)
773 self._seenparams.add(pname)
752 # status of the part's generation:
774 # status of the part's generation:
753 # - None: not started,
775 # - None: not started,
754 # - False: currently generated,
776 # - False: currently generated,
755 # - True: generation done.
777 # - True: generation done.
756 self._generated = None
778 self._generated = None
757 self.mandatory = mandatory
779 self.mandatory = mandatory
758
780
759 def copy(self):
781 def copy(self):
760 """return a copy of the part
782 """return a copy of the part
761
783
762 The new part have the very same content but no partid assigned yet.
784 The new part have the very same content but no partid assigned yet.
763 Parts with generated data cannot be copied."""
785 Parts with generated data cannot be copied."""
764 assert not util.safehasattr(self.data, 'next')
786 assert not util.safehasattr(self.data, 'next')
765 return self.__class__(self.type, self._mandatoryparams,
787 return self.__class__(self.type, self._mandatoryparams,
766 self._advisoryparams, self._data, self.mandatory)
788 self._advisoryparams, self._data, self.mandatory)
767
789
768 # methods used to defines the part content
790 # methods used to defines the part content
769 def __setdata(self, data):
791 def __setdata(self, data):
770 if self._generated is not None:
792 if self._generated is not None:
771 raise error.ReadOnlyPartError('part is being generated')
793 raise error.ReadOnlyPartError('part is being generated')
772 self._data = data
794 self._data = data
773 def __getdata(self):
795 def __getdata(self):
774 return self._data
796 return self._data
775 data = property(__getdata, __setdata)
797 data = property(__getdata, __setdata)
776
798
777 @property
799 @property
778 def mandatoryparams(self):
800 def mandatoryparams(self):
779 # make it an immutable tuple to force people through ``addparam``
801 # make it an immutable tuple to force people through ``addparam``
780 return tuple(self._mandatoryparams)
802 return tuple(self._mandatoryparams)
781
803
782 @property
804 @property
783 def advisoryparams(self):
805 def advisoryparams(self):
784 # make it an immutable tuple to force people through ``addparam``
806 # make it an immutable tuple to force people through ``addparam``
785 return tuple(self._advisoryparams)
807 return tuple(self._advisoryparams)
786
808
787 def addparam(self, name, value='', mandatory=True):
809 def addparam(self, name, value='', mandatory=True):
788 if self._generated is not None:
810 if self._generated is not None:
789 raise error.ReadOnlyPartError('part is being generated')
811 raise error.ReadOnlyPartError('part is being generated')
790 if name in self._seenparams:
812 if name in self._seenparams:
791 raise ValueError('duplicated params: %s' % name)
813 raise ValueError('duplicated params: %s' % name)
792 self._seenparams.add(name)
814 self._seenparams.add(name)
793 params = self._advisoryparams
815 params = self._advisoryparams
794 if mandatory:
816 if mandatory:
795 params = self._mandatoryparams
817 params = self._mandatoryparams
796 params.append((name, value))
818 params.append((name, value))
797
819
798 # methods used to generates the bundle2 stream
820 # methods used to generates the bundle2 stream
799 def getchunks(self, ui):
821 def getchunks(self, ui):
800 if self._generated is not None:
822 if self._generated is not None:
801 raise RuntimeError('part can only be consumed once')
823 raise RuntimeError('part can only be consumed once')
802 self._generated = False
824 self._generated = False
803
825
804 if ui.debugflag:
826 if ui.debugflag:
805 msg = ['bundle2-output-part: "%s"' % self.type]
827 msg = ['bundle2-output-part: "%s"' % self.type]
806 if not self.mandatory:
828 if not self.mandatory:
807 msg.append(' (advisory)')
829 msg.append(' (advisory)')
808 nbmp = len(self.mandatoryparams)
830 nbmp = len(self.mandatoryparams)
809 nbap = len(self.advisoryparams)
831 nbap = len(self.advisoryparams)
810 if nbmp or nbap:
832 if nbmp or nbap:
811 msg.append(' (params:')
833 msg.append(' (params:')
812 if nbmp:
834 if nbmp:
813 msg.append(' %i mandatory' % nbmp)
835 msg.append(' %i mandatory' % nbmp)
814 if nbap:
836 if nbap:
815 msg.append(' %i advisory' % nbmp)
837 msg.append(' %i advisory' % nbmp)
816 msg.append(')')
838 msg.append(')')
817 if not self.data:
839 if not self.data:
818 msg.append(' empty payload')
840 msg.append(' empty payload')
819 elif util.safehasattr(self.data, 'next'):
841 elif util.safehasattr(self.data, 'next'):
820 msg.append(' streamed payload')
842 msg.append(' streamed payload')
821 else:
843 else:
822 msg.append(' %i bytes payload' % len(self.data))
844 msg.append(' %i bytes payload' % len(self.data))
823 msg.append('\n')
845 msg.append('\n')
824 ui.debug(''.join(msg))
846 ui.debug(''.join(msg))
825
847
826 #### header
848 #### header
827 if self.mandatory:
849 if self.mandatory:
828 parttype = self.type.upper()
850 parttype = self.type.upper()
829 else:
851 else:
830 parttype = self.type.lower()
852 parttype = self.type.lower()
831 outdebug(ui, 'part %s: "%s"' % (self.id, parttype))
853 outdebug(ui, 'part %s: "%s"' % (self.id, parttype))
832 ## parttype
854 ## parttype
833 header = [_pack(_fparttypesize, len(parttype)),
855 header = [_pack(_fparttypesize, len(parttype)),
834 parttype, _pack(_fpartid, self.id),
856 parttype, _pack(_fpartid, self.id),
835 ]
857 ]
836 ## parameters
858 ## parameters
837 # count
859 # count
838 manpar = self.mandatoryparams
860 manpar = self.mandatoryparams
839 advpar = self.advisoryparams
861 advpar = self.advisoryparams
840 header.append(_pack(_fpartparamcount, len(manpar), len(advpar)))
862 header.append(_pack(_fpartparamcount, len(manpar), len(advpar)))
841 # size
863 # size
842 parsizes = []
864 parsizes = []
843 for key, value in manpar:
865 for key, value in manpar:
844 parsizes.append(len(key))
866 parsizes.append(len(key))
845 parsizes.append(len(value))
867 parsizes.append(len(value))
846 for key, value in advpar:
868 for key, value in advpar:
847 parsizes.append(len(key))
869 parsizes.append(len(key))
848 parsizes.append(len(value))
870 parsizes.append(len(value))
849 paramsizes = _pack(_makefpartparamsizes(len(parsizes) / 2), *parsizes)
871 paramsizes = _pack(_makefpartparamsizes(len(parsizes) / 2), *parsizes)
850 header.append(paramsizes)
872 header.append(paramsizes)
851 # key, value
873 # key, value
852 for key, value in manpar:
874 for key, value in manpar:
853 header.append(key)
875 header.append(key)
854 header.append(value)
876 header.append(value)
855 for key, value in advpar:
877 for key, value in advpar:
856 header.append(key)
878 header.append(key)
857 header.append(value)
879 header.append(value)
858 ## finalize header
880 ## finalize header
859 headerchunk = ''.join(header)
881 headerchunk = ''.join(header)
860 outdebug(ui, 'header chunk size: %i' % len(headerchunk))
882 outdebug(ui, 'header chunk size: %i' % len(headerchunk))
861 yield _pack(_fpartheadersize, len(headerchunk))
883 yield _pack(_fpartheadersize, len(headerchunk))
862 yield headerchunk
884 yield headerchunk
863 ## payload
885 ## payload
864 try:
886 try:
865 for chunk in self._payloadchunks():
887 for chunk in self._payloadchunks():
866 outdebug(ui, 'payload chunk size: %i' % len(chunk))
888 outdebug(ui, 'payload chunk size: %i' % len(chunk))
867 yield _pack(_fpayloadsize, len(chunk))
889 yield _pack(_fpayloadsize, len(chunk))
868 yield chunk
890 yield chunk
869 except GeneratorExit:
891 except GeneratorExit:
870 # GeneratorExit means that nobody is listening for our
892 # GeneratorExit means that nobody is listening for our
871 # results anyway, so just bail quickly rather than trying
893 # results anyway, so just bail quickly rather than trying
872 # to produce an error part.
894 # to produce an error part.
873 ui.debug('bundle2-generatorexit\n')
895 ui.debug('bundle2-generatorexit\n')
874 raise
896 raise
875 except BaseException as exc:
897 except BaseException as exc:
876 # backup exception data for later
898 # backup exception data for later
877 ui.debug('bundle2-input-stream-interrupt: encoding exception %s'
899 ui.debug('bundle2-input-stream-interrupt: encoding exception %s'
878 % exc)
900 % exc)
879 exc_info = sys.exc_info()
901 exc_info = sys.exc_info()
880 msg = 'unexpected error: %s' % exc
902 msg = 'unexpected error: %s' % exc
881 interpart = bundlepart('error:abort', [('message', msg)],
903 interpart = bundlepart('error:abort', [('message', msg)],
882 mandatory=False)
904 mandatory=False)
883 interpart.id = 0
905 interpart.id = 0
884 yield _pack(_fpayloadsize, -1)
906 yield _pack(_fpayloadsize, -1)
885 for chunk in interpart.getchunks(ui=ui):
907 for chunk in interpart.getchunks(ui=ui):
886 yield chunk
908 yield chunk
887 outdebug(ui, 'closing payload chunk')
909 outdebug(ui, 'closing payload chunk')
888 # abort current part payload
910 # abort current part payload
889 yield _pack(_fpayloadsize, 0)
911 yield _pack(_fpayloadsize, 0)
890 raise exc_info[0], exc_info[1], exc_info[2]
912 raise exc_info[0], exc_info[1], exc_info[2]
891 # end of payload
913 # end of payload
892 outdebug(ui, 'closing payload chunk')
914 outdebug(ui, 'closing payload chunk')
893 yield _pack(_fpayloadsize, 0)
915 yield _pack(_fpayloadsize, 0)
894 self._generated = True
916 self._generated = True
895
917
896 def _payloadchunks(self):
918 def _payloadchunks(self):
897 """yield chunks of a the part payload
919 """yield chunks of a the part payload
898
920
899 Exists to handle the different methods to provide data to a part."""
921 Exists to handle the different methods to provide data to a part."""
900 # we only support fixed size data now.
922 # we only support fixed size data now.
901 # This will be improved in the future.
923 # This will be improved in the future.
902 if util.safehasattr(self.data, 'next'):
924 if util.safehasattr(self.data, 'next'):
903 buff = util.chunkbuffer(self.data)
925 buff = util.chunkbuffer(self.data)
904 chunk = buff.read(preferedchunksize)
926 chunk = buff.read(preferedchunksize)
905 while chunk:
927 while chunk:
906 yield chunk
928 yield chunk
907 chunk = buff.read(preferedchunksize)
929 chunk = buff.read(preferedchunksize)
908 elif len(self.data):
930 elif len(self.data):
909 yield self.data
931 yield self.data
910
932
911
933
912 flaginterrupt = -1
934 flaginterrupt = -1
913
935
914 class interrupthandler(unpackermixin):
936 class interrupthandler(unpackermixin):
915 """read one part and process it with restricted capability
937 """read one part and process it with restricted capability
916
938
917 This allows to transmit exception raised on the producer size during part
939 This allows to transmit exception raised on the producer size during part
918 iteration while the consumer is reading a part.
940 iteration while the consumer is reading a part.
919
941
920 Part processed in this manner only have access to a ui object,"""
942 Part processed in this manner only have access to a ui object,"""
921
943
922 def __init__(self, ui, fp):
944 def __init__(self, ui, fp):
923 super(interrupthandler, self).__init__(fp)
945 super(interrupthandler, self).__init__(fp)
924 self.ui = ui
946 self.ui = ui
925
947
926 def _readpartheader(self):
948 def _readpartheader(self):
927 """reads a part header size and return the bytes blob
949 """reads a part header size and return the bytes blob
928
950
929 returns None if empty"""
951 returns None if empty"""
930 headersize = self._unpack(_fpartheadersize)[0]
952 headersize = self._unpack(_fpartheadersize)[0]
931 if headersize < 0:
953 if headersize < 0:
932 raise error.BundleValueError('negative part header size: %i'
954 raise error.BundleValueError('negative part header size: %i'
933 % headersize)
955 % headersize)
934 indebug(self.ui, 'part header size: %i\n' % headersize)
956 indebug(self.ui, 'part header size: %i\n' % headersize)
935 if headersize:
957 if headersize:
936 return self._readexact(headersize)
958 return self._readexact(headersize)
937 return None
959 return None
938
960
939 def __call__(self):
961 def __call__(self):
940
962
941 self.ui.debug('bundle2-input-stream-interrupt:'
963 self.ui.debug('bundle2-input-stream-interrupt:'
942 ' opening out of band context\n')
964 ' opening out of band context\n')
943 indebug(self.ui, 'bundle2 stream interruption, looking for a part.')
965 indebug(self.ui, 'bundle2 stream interruption, looking for a part.')
944 headerblock = self._readpartheader()
966 headerblock = self._readpartheader()
945 if headerblock is None:
967 if headerblock is None:
946 indebug(self.ui, 'no part found during interruption.')
968 indebug(self.ui, 'no part found during interruption.')
947 return
969 return
948 part = unbundlepart(self.ui, headerblock, self._fp)
970 part = unbundlepart(self.ui, headerblock, self._fp)
949 op = interruptoperation(self.ui)
971 op = interruptoperation(self.ui)
950 _processpart(op, part)
972 _processpart(op, part)
951 self.ui.debug('bundle2-input-stream-interrupt:'
973 self.ui.debug('bundle2-input-stream-interrupt:'
952 ' closing out of band context\n')
974 ' closing out of band context\n')
953
975
954 class interruptoperation(object):
976 class interruptoperation(object):
955 """A limited operation to be use by part handler during interruption
977 """A limited operation to be use by part handler during interruption
956
978
957 It only have access to an ui object.
979 It only have access to an ui object.
958 """
980 """
959
981
960 def __init__(self, ui):
982 def __init__(self, ui):
961 self.ui = ui
983 self.ui = ui
962 self.reply = None
984 self.reply = None
963 self.captureoutput = False
985 self.captureoutput = False
964
986
965 @property
987 @property
966 def repo(self):
988 def repo(self):
967 raise RuntimeError('no repo access from stream interruption')
989 raise RuntimeError('no repo access from stream interruption')
968
990
969 def gettransaction(self):
991 def gettransaction(self):
970 raise TransactionUnavailable('no repo access from stream interruption')
992 raise TransactionUnavailable('no repo access from stream interruption')
971
993
972 class unbundlepart(unpackermixin):
994 class unbundlepart(unpackermixin):
973 """a bundle part read from a bundle"""
995 """a bundle part read from a bundle"""
974
996
975 def __init__(self, ui, header, fp):
997 def __init__(self, ui, header, fp):
976 super(unbundlepart, self).__init__(fp)
998 super(unbundlepart, self).__init__(fp)
977 self.ui = ui
999 self.ui = ui
978 # unbundle state attr
1000 # unbundle state attr
979 self._headerdata = header
1001 self._headerdata = header
980 self._headeroffset = 0
1002 self._headeroffset = 0
981 self._initialized = False
1003 self._initialized = False
982 self.consumed = False
1004 self.consumed = False
983 # part data
1005 # part data
984 self.id = None
1006 self.id = None
985 self.type = None
1007 self.type = None
986 self.mandatoryparams = None
1008 self.mandatoryparams = None
987 self.advisoryparams = None
1009 self.advisoryparams = None
988 self.params = None
1010 self.params = None
989 self.mandatorykeys = ()
1011 self.mandatorykeys = ()
990 self._payloadstream = None
1012 self._payloadstream = None
991 self._readheader()
1013 self._readheader()
992 self._mandatory = None
1014 self._mandatory = None
993 self._chunkindex = [] #(payload, file) position tuples for chunk starts
1015 self._chunkindex = [] #(payload, file) position tuples for chunk starts
994 self._pos = 0
1016 self._pos = 0
995
1017
996 def _fromheader(self, size):
1018 def _fromheader(self, size):
997 """return the next <size> byte from the header"""
1019 """return the next <size> byte from the header"""
998 offset = self._headeroffset
1020 offset = self._headeroffset
999 data = self._headerdata[offset:(offset + size)]
1021 data = self._headerdata[offset:(offset + size)]
1000 self._headeroffset = offset + size
1022 self._headeroffset = offset + size
1001 return data
1023 return data
1002
1024
1003 def _unpackheader(self, format):
1025 def _unpackheader(self, format):
1004 """read given format from header
1026 """read given format from header
1005
1027
1006 This automatically compute the size of the format to read."""
1028 This automatically compute the size of the format to read."""
1007 data = self._fromheader(struct.calcsize(format))
1029 data = self._fromheader(struct.calcsize(format))
1008 return _unpack(format, data)
1030 return _unpack(format, data)
1009
1031
1010 def _initparams(self, mandatoryparams, advisoryparams):
1032 def _initparams(self, mandatoryparams, advisoryparams):
1011 """internal function to setup all logic related parameters"""
1033 """internal function to setup all logic related parameters"""
1012 # make it read only to prevent people touching it by mistake.
1034 # make it read only to prevent people touching it by mistake.
1013 self.mandatoryparams = tuple(mandatoryparams)
1035 self.mandatoryparams = tuple(mandatoryparams)
1014 self.advisoryparams = tuple(advisoryparams)
1036 self.advisoryparams = tuple(advisoryparams)
1015 # user friendly UI
1037 # user friendly UI
1016 self.params = dict(self.mandatoryparams)
1038 self.params = dict(self.mandatoryparams)
1017 self.params.update(dict(self.advisoryparams))
1039 self.params.update(dict(self.advisoryparams))
1018 self.mandatorykeys = frozenset(p[0] for p in mandatoryparams)
1040 self.mandatorykeys = frozenset(p[0] for p in mandatoryparams)
1019
1041
1020 def _payloadchunks(self, chunknum=0):
1042 def _payloadchunks(self, chunknum=0):
1021 '''seek to specified chunk and start yielding data'''
1043 '''seek to specified chunk and start yielding data'''
1022 if len(self._chunkindex) == 0:
1044 if len(self._chunkindex) == 0:
1023 assert chunknum == 0, 'Must start with chunk 0'
1045 assert chunknum == 0, 'Must start with chunk 0'
1024 self._chunkindex.append((0, super(unbundlepart, self).tell()))
1046 self._chunkindex.append((0, super(unbundlepart, self).tell()))
1025 else:
1047 else:
1026 assert chunknum < len(self._chunkindex), \
1048 assert chunknum < len(self._chunkindex), \
1027 'Unknown chunk %d' % chunknum
1049 'Unknown chunk %d' % chunknum
1028 super(unbundlepart, self).seek(self._chunkindex[chunknum][1])
1050 super(unbundlepart, self).seek(self._chunkindex[chunknum][1])
1029
1051
1030 pos = self._chunkindex[chunknum][0]
1052 pos = self._chunkindex[chunknum][0]
1031 payloadsize = self._unpack(_fpayloadsize)[0]
1053 payloadsize = self._unpack(_fpayloadsize)[0]
1032 indebug(self.ui, 'payload chunk size: %i' % payloadsize)
1054 indebug(self.ui, 'payload chunk size: %i' % payloadsize)
1033 while payloadsize:
1055 while payloadsize:
1034 if payloadsize == flaginterrupt:
1056 if payloadsize == flaginterrupt:
1035 # interruption detection, the handler will now read a
1057 # interruption detection, the handler will now read a
1036 # single part and process it.
1058 # single part and process it.
1037 interrupthandler(self.ui, self._fp)()
1059 interrupthandler(self.ui, self._fp)()
1038 elif payloadsize < 0:
1060 elif payloadsize < 0:
1039 msg = 'negative payload chunk size: %i' % payloadsize
1061 msg = 'negative payload chunk size: %i' % payloadsize
1040 raise error.BundleValueError(msg)
1062 raise error.BundleValueError(msg)
1041 else:
1063 else:
1042 result = self._readexact(payloadsize)
1064 result = self._readexact(payloadsize)
1043 chunknum += 1
1065 chunknum += 1
1044 pos += payloadsize
1066 pos += payloadsize
1045 if chunknum == len(self._chunkindex):
1067 if chunknum == len(self._chunkindex):
1046 self._chunkindex.append((pos,
1068 self._chunkindex.append((pos,
1047 super(unbundlepart, self).tell()))
1069 super(unbundlepart, self).tell()))
1048 yield result
1070 yield result
1049 payloadsize = self._unpack(_fpayloadsize)[0]
1071 payloadsize = self._unpack(_fpayloadsize)[0]
1050 indebug(self.ui, 'payload chunk size: %i' % payloadsize)
1072 indebug(self.ui, 'payload chunk size: %i' % payloadsize)
1051
1073
1052 def _findchunk(self, pos):
1074 def _findchunk(self, pos):
1053 '''for a given payload position, return a chunk number and offset'''
1075 '''for a given payload position, return a chunk number and offset'''
1054 for chunk, (ppos, fpos) in enumerate(self._chunkindex):
1076 for chunk, (ppos, fpos) in enumerate(self._chunkindex):
1055 if ppos == pos:
1077 if ppos == pos:
1056 return chunk, 0
1078 return chunk, 0
1057 elif ppos > pos:
1079 elif ppos > pos:
1058 return chunk - 1, pos - self._chunkindex[chunk - 1][0]
1080 return chunk - 1, pos - self._chunkindex[chunk - 1][0]
1059 raise ValueError('Unknown chunk')
1081 raise ValueError('Unknown chunk')
1060
1082
1061 def _readheader(self):
1083 def _readheader(self):
1062 """read the header and setup the object"""
1084 """read the header and setup the object"""
1063 typesize = self._unpackheader(_fparttypesize)[0]
1085 typesize = self._unpackheader(_fparttypesize)[0]
1064 self.type = self._fromheader(typesize)
1086 self.type = self._fromheader(typesize)
1065 indebug(self.ui, 'part type: "%s"' % self.type)
1087 indebug(self.ui, 'part type: "%s"' % self.type)
1066 self.id = self._unpackheader(_fpartid)[0]
1088 self.id = self._unpackheader(_fpartid)[0]
1067 indebug(self.ui, 'part id: "%s"' % self.id)
1089 indebug(self.ui, 'part id: "%s"' % self.id)
1068 # extract mandatory bit from type
1090 # extract mandatory bit from type
1069 self.mandatory = (self.type != self.type.lower())
1091 self.mandatory = (self.type != self.type.lower())
1070 self.type = self.type.lower()
1092 self.type = self.type.lower()
1071 ## reading parameters
1093 ## reading parameters
1072 # param count
1094 # param count
1073 mancount, advcount = self._unpackheader(_fpartparamcount)
1095 mancount, advcount = self._unpackheader(_fpartparamcount)
1074 indebug(self.ui, 'part parameters: %i' % (mancount + advcount))
1096 indebug(self.ui, 'part parameters: %i' % (mancount + advcount))
1075 # param size
1097 # param size
1076 fparamsizes = _makefpartparamsizes(mancount + advcount)
1098 fparamsizes = _makefpartparamsizes(mancount + advcount)
1077 paramsizes = self._unpackheader(fparamsizes)
1099 paramsizes = self._unpackheader(fparamsizes)
1078 # make it a list of couple again
1100 # make it a list of couple again
1079 paramsizes = zip(paramsizes[::2], paramsizes[1::2])
1101 paramsizes = zip(paramsizes[::2], paramsizes[1::2])
1080 # split mandatory from advisory
1102 # split mandatory from advisory
1081 mansizes = paramsizes[:mancount]
1103 mansizes = paramsizes[:mancount]
1082 advsizes = paramsizes[mancount:]
1104 advsizes = paramsizes[mancount:]
1083 # retrieve param value
1105 # retrieve param value
1084 manparams = []
1106 manparams = []
1085 for key, value in mansizes:
1107 for key, value in mansizes:
1086 manparams.append((self._fromheader(key), self._fromheader(value)))
1108 manparams.append((self._fromheader(key), self._fromheader(value)))
1087 advparams = []
1109 advparams = []
1088 for key, value in advsizes:
1110 for key, value in advsizes:
1089 advparams.append((self._fromheader(key), self._fromheader(value)))
1111 advparams.append((self._fromheader(key), self._fromheader(value)))
1090 self._initparams(manparams, advparams)
1112 self._initparams(manparams, advparams)
1091 ## part payload
1113 ## part payload
1092 self._payloadstream = util.chunkbuffer(self._payloadchunks())
1114 self._payloadstream = util.chunkbuffer(self._payloadchunks())
1093 # we read the data, tell it
1115 # we read the data, tell it
1094 self._initialized = True
1116 self._initialized = True
1095
1117
1096 def read(self, size=None):
1118 def read(self, size=None):
1097 """read payload data"""
1119 """read payload data"""
1098 if not self._initialized:
1120 if not self._initialized:
1099 self._readheader()
1121 self._readheader()
1100 if size is None:
1122 if size is None:
1101 data = self._payloadstream.read()
1123 data = self._payloadstream.read()
1102 else:
1124 else:
1103 data = self._payloadstream.read(size)
1125 data = self._payloadstream.read(size)
1104 self._pos += len(data)
1126 self._pos += len(data)
1105 if size is None or len(data) < size:
1127 if size is None or len(data) < size:
1106 if not self.consumed and self._pos:
1128 if not self.consumed and self._pos:
1107 self.ui.debug('bundle2-input-part: total payload size %i\n'
1129 self.ui.debug('bundle2-input-part: total payload size %i\n'
1108 % self._pos)
1130 % self._pos)
1109 self.consumed = True
1131 self.consumed = True
1110 return data
1132 return data
1111
1133
1112 def tell(self):
1134 def tell(self):
1113 return self._pos
1135 return self._pos
1114
1136
1115 def seek(self, offset, whence=0):
1137 def seek(self, offset, whence=0):
1116 if whence == 0:
1138 if whence == 0:
1117 newpos = offset
1139 newpos = offset
1118 elif whence == 1:
1140 elif whence == 1:
1119 newpos = self._pos + offset
1141 newpos = self._pos + offset
1120 elif whence == 2:
1142 elif whence == 2:
1121 if not self.consumed:
1143 if not self.consumed:
1122 self.read()
1144 self.read()
1123 newpos = self._chunkindex[-1][0] - offset
1145 newpos = self._chunkindex[-1][0] - offset
1124 else:
1146 else:
1125 raise ValueError('Unknown whence value: %r' % (whence,))
1147 raise ValueError('Unknown whence value: %r' % (whence,))
1126
1148
1127 if newpos > self._chunkindex[-1][0] and not self.consumed:
1149 if newpos > self._chunkindex[-1][0] and not self.consumed:
1128 self.read()
1150 self.read()
1129 if not 0 <= newpos <= self._chunkindex[-1][0]:
1151 if not 0 <= newpos <= self._chunkindex[-1][0]:
1130 raise ValueError('Offset out of range')
1152 raise ValueError('Offset out of range')
1131
1153
1132 if self._pos != newpos:
1154 if self._pos != newpos:
1133 chunk, internaloffset = self._findchunk(newpos)
1155 chunk, internaloffset = self._findchunk(newpos)
1134 self._payloadstream = util.chunkbuffer(self._payloadchunks(chunk))
1156 self._payloadstream = util.chunkbuffer(self._payloadchunks(chunk))
1135 adjust = self.read(internaloffset)
1157 adjust = self.read(internaloffset)
1136 if len(adjust) != internaloffset:
1158 if len(adjust) != internaloffset:
1137 raise util.Abort(_('Seek failed\n'))
1159 raise util.Abort(_('Seek failed\n'))
1138 self._pos = newpos
1160 self._pos = newpos
1139
1161
1140 # These are only the static capabilities.
1162 # These are only the static capabilities.
1141 # Check the 'getrepocaps' function for the rest.
1163 # Check the 'getrepocaps' function for the rest.
1142 capabilities = {'HG20': (),
1164 capabilities = {'HG20': (),
1143 'error': ('abort', 'unsupportedcontent', 'pushraced',
1165 'error': ('abort', 'unsupportedcontent', 'pushraced',
1144 'pushkey'),
1166 'pushkey'),
1145 'listkeys': (),
1167 'listkeys': (),
1146 'pushkey': (),
1168 'pushkey': (),
1147 'digests': tuple(sorted(util.DIGESTS.keys())),
1169 'digests': tuple(sorted(util.DIGESTS.keys())),
1148 'remote-changegroup': ('http', 'https'),
1170 'remote-changegroup': ('http', 'https'),
1149 'hgtagsfnodes': (),
1171 'hgtagsfnodes': (),
1150 }
1172 }
1151
1173
1152 def getrepocaps(repo, allowpushback=False):
1174 def getrepocaps(repo, allowpushback=False):
1153 """return the bundle2 capabilities for a given repo
1175 """return the bundle2 capabilities for a given repo
1154
1176
1155 Exists to allow extensions (like evolution) to mutate the capabilities.
1177 Exists to allow extensions (like evolution) to mutate the capabilities.
1156 """
1178 """
1157 caps = capabilities.copy()
1179 caps = capabilities.copy()
1158 caps['changegroup'] = tuple(sorted(changegroup.packermap.keys()))
1180 caps['changegroup'] = tuple(sorted(changegroup.packermap.keys()))
1159 if obsolete.isenabled(repo, obsolete.exchangeopt):
1181 if obsolete.isenabled(repo, obsolete.exchangeopt):
1160 supportedformat = tuple('V%i' % v for v in obsolete.formats)
1182 supportedformat = tuple('V%i' % v for v in obsolete.formats)
1161 caps['obsmarkers'] = supportedformat
1183 caps['obsmarkers'] = supportedformat
1162 if allowpushback:
1184 if allowpushback:
1163 caps['pushback'] = ()
1185 caps['pushback'] = ()
1164 return caps
1186 return caps
1165
1187
1166 def bundle2caps(remote):
1188 def bundle2caps(remote):
1167 """return the bundle capabilities of a peer as dict"""
1189 """return the bundle capabilities of a peer as dict"""
1168 raw = remote.capable('bundle2')
1190 raw = remote.capable('bundle2')
1169 if not raw and raw != '':
1191 if not raw and raw != '':
1170 return {}
1192 return {}
1171 capsblob = urllib.unquote(remote.capable('bundle2'))
1193 capsblob = urllib.unquote(remote.capable('bundle2'))
1172 return decodecaps(capsblob)
1194 return decodecaps(capsblob)
1173
1195
1174 def obsmarkersversion(caps):
1196 def obsmarkersversion(caps):
1175 """extract the list of supported obsmarkers versions from a bundle2caps dict
1197 """extract the list of supported obsmarkers versions from a bundle2caps dict
1176 """
1198 """
1177 obscaps = caps.get('obsmarkers', ())
1199 obscaps = caps.get('obsmarkers', ())
1178 return [int(c[1:]) for c in obscaps if c.startswith('V')]
1200 return [int(c[1:]) for c in obscaps if c.startswith('V')]
1179
1201
1180 @parthandler('changegroup', ('version', 'nbchanges'))
1202 @parthandler('changegroup', ('version', 'nbchanges'))
1181 def handlechangegroup(op, inpart):
1203 def handlechangegroup(op, inpart):
1182 """apply a changegroup part on the repo
1204 """apply a changegroup part on the repo
1183
1205
1184 This is a very early implementation that will massive rework before being
1206 This is a very early implementation that will massive rework before being
1185 inflicted to any end-user.
1207 inflicted to any end-user.
1186 """
1208 """
1187 # Make sure we trigger a transaction creation
1209 # Make sure we trigger a transaction creation
1188 #
1210 #
1189 # The addchangegroup function will get a transaction object by itself, but
1211 # The addchangegroup function will get a transaction object by itself, but
1190 # we need to make sure we trigger the creation of a transaction object used
1212 # we need to make sure we trigger the creation of a transaction object used
1191 # for the whole processing scope.
1213 # for the whole processing scope.
1192 op.gettransaction()
1214 op.gettransaction()
1193 unpackerversion = inpart.params.get('version', '01')
1215 unpackerversion = inpart.params.get('version', '01')
1194 # We should raise an appropriate exception here
1216 # We should raise an appropriate exception here
1195 unpacker = changegroup.packermap[unpackerversion][1]
1217 unpacker = changegroup.packermap[unpackerversion][1]
1196 cg = unpacker(inpart, None)
1218 cg = unpacker(inpart, None)
1197 # the source and url passed here are overwritten by the one contained in
1219 # the source and url passed here are overwritten by the one contained in
1198 # the transaction.hookargs argument. So 'bundle2' is a placeholder
1220 # the transaction.hookargs argument. So 'bundle2' is a placeholder
1199 nbchangesets = None
1221 nbchangesets = None
1200 if 'nbchanges' in inpart.params:
1222 if 'nbchanges' in inpart.params:
1201 nbchangesets = int(inpart.params.get('nbchanges'))
1223 nbchangesets = int(inpart.params.get('nbchanges'))
1202 ret = changegroup.addchangegroup(op.repo, cg, 'bundle2', 'bundle2',
1224 ret = changegroup.addchangegroup(op.repo, cg, 'bundle2', 'bundle2',
1203 expectedtotal=nbchangesets)
1225 expectedtotal=nbchangesets)
1204 op.records.add('changegroup', {'return': ret})
1226 op.records.add('changegroup', {'return': ret})
1205 if op.reply is not None:
1227 if op.reply is not None:
1206 # This is definitely not the final form of this
1228 # This is definitely not the final form of this
1207 # return. But one need to start somewhere.
1229 # return. But one need to start somewhere.
1208 part = op.reply.newpart('reply:changegroup', mandatory=False)
1230 part = op.reply.newpart('reply:changegroup', mandatory=False)
1209 part.addparam('in-reply-to', str(inpart.id), mandatory=False)
1231 part.addparam('in-reply-to', str(inpart.id), mandatory=False)
1210 part.addparam('return', '%i' % ret, mandatory=False)
1232 part.addparam('return', '%i' % ret, mandatory=False)
1211 assert not inpart.read()
1233 assert not inpart.read()
1212
1234
1213 _remotechangegroupparams = tuple(['url', 'size', 'digests'] +
1235 _remotechangegroupparams = tuple(['url', 'size', 'digests'] +
1214 ['digest:%s' % k for k in util.DIGESTS.keys()])
1236 ['digest:%s' % k for k in util.DIGESTS.keys()])
1215 @parthandler('remote-changegroup', _remotechangegroupparams)
1237 @parthandler('remote-changegroup', _remotechangegroupparams)
1216 def handleremotechangegroup(op, inpart):
1238 def handleremotechangegroup(op, inpart):
1217 """apply a bundle10 on the repo, given an url and validation information
1239 """apply a bundle10 on the repo, given an url and validation information
1218
1240
1219 All the information about the remote bundle to import are given as
1241 All the information about the remote bundle to import are given as
1220 parameters. The parameters include:
1242 parameters. The parameters include:
1221 - url: the url to the bundle10.
1243 - url: the url to the bundle10.
1222 - size: the bundle10 file size. It is used to validate what was
1244 - size: the bundle10 file size. It is used to validate what was
1223 retrieved by the client matches the server knowledge about the bundle.
1245 retrieved by the client matches the server knowledge about the bundle.
1224 - digests: a space separated list of the digest types provided as
1246 - digests: a space separated list of the digest types provided as
1225 parameters.
1247 parameters.
1226 - digest:<digest-type>: the hexadecimal representation of the digest with
1248 - digest:<digest-type>: the hexadecimal representation of the digest with
1227 that name. Like the size, it is used to validate what was retrieved by
1249 that name. Like the size, it is used to validate what was retrieved by
1228 the client matches what the server knows about the bundle.
1250 the client matches what the server knows about the bundle.
1229
1251
1230 When multiple digest types are given, all of them are checked.
1252 When multiple digest types are given, all of them are checked.
1231 """
1253 """
1232 try:
1254 try:
1233 raw_url = inpart.params['url']
1255 raw_url = inpart.params['url']
1234 except KeyError:
1256 except KeyError:
1235 raise util.Abort(_('remote-changegroup: missing "%s" param') % 'url')
1257 raise util.Abort(_('remote-changegroup: missing "%s" param') % 'url')
1236 parsed_url = util.url(raw_url)
1258 parsed_url = util.url(raw_url)
1237 if parsed_url.scheme not in capabilities['remote-changegroup']:
1259 if parsed_url.scheme not in capabilities['remote-changegroup']:
1238 raise util.Abort(_('remote-changegroup does not support %s urls') %
1260 raise util.Abort(_('remote-changegroup does not support %s urls') %
1239 parsed_url.scheme)
1261 parsed_url.scheme)
1240
1262
1241 try:
1263 try:
1242 size = int(inpart.params['size'])
1264 size = int(inpart.params['size'])
1243 except ValueError:
1265 except ValueError:
1244 raise util.Abort(_('remote-changegroup: invalid value for param "%s"')
1266 raise util.Abort(_('remote-changegroup: invalid value for param "%s"')
1245 % 'size')
1267 % 'size')
1246 except KeyError:
1268 except KeyError:
1247 raise util.Abort(_('remote-changegroup: missing "%s" param') % 'size')
1269 raise util.Abort(_('remote-changegroup: missing "%s" param') % 'size')
1248
1270
1249 digests = {}
1271 digests = {}
1250 for typ in inpart.params.get('digests', '').split():
1272 for typ in inpart.params.get('digests', '').split():
1251 param = 'digest:%s' % typ
1273 param = 'digest:%s' % typ
1252 try:
1274 try:
1253 value = inpart.params[param]
1275 value = inpart.params[param]
1254 except KeyError:
1276 except KeyError:
1255 raise util.Abort(_('remote-changegroup: missing "%s" param') %
1277 raise util.Abort(_('remote-changegroup: missing "%s" param') %
1256 param)
1278 param)
1257 digests[typ] = value
1279 digests[typ] = value
1258
1280
1259 real_part = util.digestchecker(url.open(op.ui, raw_url), size, digests)
1281 real_part = util.digestchecker(url.open(op.ui, raw_url), size, digests)
1260
1282
1261 # Make sure we trigger a transaction creation
1283 # Make sure we trigger a transaction creation
1262 #
1284 #
1263 # The addchangegroup function will get a transaction object by itself, but
1285 # The addchangegroup function will get a transaction object by itself, but
1264 # we need to make sure we trigger the creation of a transaction object used
1286 # we need to make sure we trigger the creation of a transaction object used
1265 # for the whole processing scope.
1287 # for the whole processing scope.
1266 op.gettransaction()
1288 op.gettransaction()
1267 from . import exchange
1289 from . import exchange
1268 cg = exchange.readbundle(op.repo.ui, real_part, raw_url)
1290 cg = exchange.readbundle(op.repo.ui, real_part, raw_url)
1269 if not isinstance(cg, changegroup.cg1unpacker):
1291 if not isinstance(cg, changegroup.cg1unpacker):
1270 raise util.Abort(_('%s: not a bundle version 1.0') %
1292 raise util.Abort(_('%s: not a bundle version 1.0') %
1271 util.hidepassword(raw_url))
1293 util.hidepassword(raw_url))
1272 ret = changegroup.addchangegroup(op.repo, cg, 'bundle2', 'bundle2')
1294 ret = changegroup.addchangegroup(op.repo, cg, 'bundle2', 'bundle2')
1273 op.records.add('changegroup', {'return': ret})
1295 op.records.add('changegroup', {'return': ret})
1274 if op.reply is not None:
1296 if op.reply is not None:
1275 # This is definitely not the final form of this
1297 # This is definitely not the final form of this
1276 # return. But one need to start somewhere.
1298 # return. But one need to start somewhere.
1277 part = op.reply.newpart('reply:changegroup')
1299 part = op.reply.newpart('reply:changegroup')
1278 part.addparam('in-reply-to', str(inpart.id), mandatory=False)
1300 part.addparam('in-reply-to', str(inpart.id), mandatory=False)
1279 part.addparam('return', '%i' % ret, mandatory=False)
1301 part.addparam('return', '%i' % ret, mandatory=False)
1280 try:
1302 try:
1281 real_part.validate()
1303 real_part.validate()
1282 except util.Abort as e:
1304 except util.Abort as e:
1283 raise util.Abort(_('bundle at %s is corrupted:\n%s') %
1305 raise util.Abort(_('bundle at %s is corrupted:\n%s') %
1284 (util.hidepassword(raw_url), str(e)))
1306 (util.hidepassword(raw_url), str(e)))
1285 assert not inpart.read()
1307 assert not inpart.read()
1286
1308
1287 @parthandler('reply:changegroup', ('return', 'in-reply-to'))
1309 @parthandler('reply:changegroup', ('return', 'in-reply-to'))
1288 def handlereplychangegroup(op, inpart):
1310 def handlereplychangegroup(op, inpart):
1289 ret = int(inpart.params['return'])
1311 ret = int(inpart.params['return'])
1290 replyto = int(inpart.params['in-reply-to'])
1312 replyto = int(inpart.params['in-reply-to'])
1291 op.records.add('changegroup', {'return': ret}, replyto)
1313 op.records.add('changegroup', {'return': ret}, replyto)
1292
1314
1293 @parthandler('check:heads')
1315 @parthandler('check:heads')
1294 def handlecheckheads(op, inpart):
1316 def handlecheckheads(op, inpart):
1295 """check that head of the repo did not change
1317 """check that head of the repo did not change
1296
1318
1297 This is used to detect a push race when using unbundle.
1319 This is used to detect a push race when using unbundle.
1298 This replaces the "heads" argument of unbundle."""
1320 This replaces the "heads" argument of unbundle."""
1299 h = inpart.read(20)
1321 h = inpart.read(20)
1300 heads = []
1322 heads = []
1301 while len(h) == 20:
1323 while len(h) == 20:
1302 heads.append(h)
1324 heads.append(h)
1303 h = inpart.read(20)
1325 h = inpart.read(20)
1304 assert not h
1326 assert not h
1305 if heads != op.repo.heads():
1327 if heads != op.repo.heads():
1306 raise error.PushRaced('repository changed while pushing - '
1328 raise error.PushRaced('repository changed while pushing - '
1307 'please try again')
1329 'please try again')
1308
1330
1309 @parthandler('output')
1331 @parthandler('output')
1310 def handleoutput(op, inpart):
1332 def handleoutput(op, inpart):
1311 """forward output captured on the server to the client"""
1333 """forward output captured on the server to the client"""
1312 for line in inpart.read().splitlines():
1334 for line in inpart.read().splitlines():
1313 op.ui.status(('remote: %s\n' % line))
1335 op.ui.status(('remote: %s\n' % line))
1314
1336
1315 @parthandler('replycaps')
1337 @parthandler('replycaps')
1316 def handlereplycaps(op, inpart):
1338 def handlereplycaps(op, inpart):
1317 """Notify that a reply bundle should be created
1339 """Notify that a reply bundle should be created
1318
1340
1319 The payload contains the capabilities information for the reply"""
1341 The payload contains the capabilities information for the reply"""
1320 caps = decodecaps(inpart.read())
1342 caps = decodecaps(inpart.read())
1321 if op.reply is None:
1343 if op.reply is None:
1322 op.reply = bundle20(op.ui, caps)
1344 op.reply = bundle20(op.ui, caps)
1323
1345
1324 @parthandler('error:abort', ('message', 'hint'))
1346 @parthandler('error:abort', ('message', 'hint'))
1325 def handleerrorabort(op, inpart):
1347 def handleerrorabort(op, inpart):
1326 """Used to transmit abort error over the wire"""
1348 """Used to transmit abort error over the wire"""
1327 raise util.Abort(inpart.params['message'], hint=inpart.params.get('hint'))
1349 raise util.Abort(inpart.params['message'], hint=inpart.params.get('hint'))
1328
1350
1329 @parthandler('error:pushkey', ('namespace', 'key', 'new', 'old', 'ret',
1351 @parthandler('error:pushkey', ('namespace', 'key', 'new', 'old', 'ret',
1330 'in-reply-to'))
1352 'in-reply-to'))
1331 def handleerrorpushkey(op, inpart):
1353 def handleerrorpushkey(op, inpart):
1332 """Used to transmit failure of a mandatory pushkey over the wire"""
1354 """Used to transmit failure of a mandatory pushkey over the wire"""
1333 kwargs = {}
1355 kwargs = {}
1334 for name in ('namespace', 'key', 'new', 'old', 'ret'):
1356 for name in ('namespace', 'key', 'new', 'old', 'ret'):
1335 value = inpart.params.get(name)
1357 value = inpart.params.get(name)
1336 if value is not None:
1358 if value is not None:
1337 kwargs[name] = value
1359 kwargs[name] = value
1338 raise error.PushkeyFailed(inpart.params['in-reply-to'], **kwargs)
1360 raise error.PushkeyFailed(inpart.params['in-reply-to'], **kwargs)
1339
1361
1340 @parthandler('error:unsupportedcontent', ('parttype', 'params'))
1362 @parthandler('error:unsupportedcontent', ('parttype', 'params'))
1341 def handleerrorunsupportedcontent(op, inpart):
1363 def handleerrorunsupportedcontent(op, inpart):
1342 """Used to transmit unknown content error over the wire"""
1364 """Used to transmit unknown content error over the wire"""
1343 kwargs = {}
1365 kwargs = {}
1344 parttype = inpart.params.get('parttype')
1366 parttype = inpart.params.get('parttype')
1345 if parttype is not None:
1367 if parttype is not None:
1346 kwargs['parttype'] = parttype
1368 kwargs['parttype'] = parttype
1347 params = inpart.params.get('params')
1369 params = inpart.params.get('params')
1348 if params is not None:
1370 if params is not None:
1349 kwargs['params'] = params.split('\0')
1371 kwargs['params'] = params.split('\0')
1350
1372
1351 raise error.BundleUnknownFeatureError(**kwargs)
1373 raise error.BundleUnknownFeatureError(**kwargs)
1352
1374
1353 @parthandler('error:pushraced', ('message',))
1375 @parthandler('error:pushraced', ('message',))
1354 def handleerrorpushraced(op, inpart):
1376 def handleerrorpushraced(op, inpart):
1355 """Used to transmit push race error over the wire"""
1377 """Used to transmit push race error over the wire"""
1356 raise error.ResponseError(_('push failed:'), inpart.params['message'])
1378 raise error.ResponseError(_('push failed:'), inpart.params['message'])
1357
1379
1358 @parthandler('listkeys', ('namespace',))
1380 @parthandler('listkeys', ('namespace',))
1359 def handlelistkeys(op, inpart):
1381 def handlelistkeys(op, inpart):
1360 """retrieve pushkey namespace content stored in a bundle2"""
1382 """retrieve pushkey namespace content stored in a bundle2"""
1361 namespace = inpart.params['namespace']
1383 namespace = inpart.params['namespace']
1362 r = pushkey.decodekeys(inpart.read())
1384 r = pushkey.decodekeys(inpart.read())
1363 op.records.add('listkeys', (namespace, r))
1385 op.records.add('listkeys', (namespace, r))
1364
1386
1365 @parthandler('pushkey', ('namespace', 'key', 'old', 'new'))
1387 @parthandler('pushkey', ('namespace', 'key', 'old', 'new'))
1366 def handlepushkey(op, inpart):
1388 def handlepushkey(op, inpart):
1367 """process a pushkey request"""
1389 """process a pushkey request"""
1368 dec = pushkey.decode
1390 dec = pushkey.decode
1369 namespace = dec(inpart.params['namespace'])
1391 namespace = dec(inpart.params['namespace'])
1370 key = dec(inpart.params['key'])
1392 key = dec(inpart.params['key'])
1371 old = dec(inpart.params['old'])
1393 old = dec(inpart.params['old'])
1372 new = dec(inpart.params['new'])
1394 new = dec(inpart.params['new'])
1373 ret = op.repo.pushkey(namespace, key, old, new)
1395 ret = op.repo.pushkey(namespace, key, old, new)
1374 record = {'namespace': namespace,
1396 record = {'namespace': namespace,
1375 'key': key,
1397 'key': key,
1376 'old': old,
1398 'old': old,
1377 'new': new}
1399 'new': new}
1378 op.records.add('pushkey', record)
1400 op.records.add('pushkey', record)
1379 if op.reply is not None:
1401 if op.reply is not None:
1380 rpart = op.reply.newpart('reply:pushkey')
1402 rpart = op.reply.newpart('reply:pushkey')
1381 rpart.addparam('in-reply-to', str(inpart.id), mandatory=False)
1403 rpart.addparam('in-reply-to', str(inpart.id), mandatory=False)
1382 rpart.addparam('return', '%i' % ret, mandatory=False)
1404 rpart.addparam('return', '%i' % ret, mandatory=False)
1383 if inpart.mandatory and not ret:
1405 if inpart.mandatory and not ret:
1384 kwargs = {}
1406 kwargs = {}
1385 for key in ('namespace', 'key', 'new', 'old', 'ret'):
1407 for key in ('namespace', 'key', 'new', 'old', 'ret'):
1386 if key in inpart.params:
1408 if key in inpart.params:
1387 kwargs[key] = inpart.params[key]
1409 kwargs[key] = inpart.params[key]
1388 raise error.PushkeyFailed(partid=str(inpart.id), **kwargs)
1410 raise error.PushkeyFailed(partid=str(inpart.id), **kwargs)
1389
1411
1390 @parthandler('reply:pushkey', ('return', 'in-reply-to'))
1412 @parthandler('reply:pushkey', ('return', 'in-reply-to'))
1391 def handlepushkeyreply(op, inpart):
1413 def handlepushkeyreply(op, inpart):
1392 """retrieve the result of a pushkey request"""
1414 """retrieve the result of a pushkey request"""
1393 ret = int(inpart.params['return'])
1415 ret = int(inpart.params['return'])
1394 partid = int(inpart.params['in-reply-to'])
1416 partid = int(inpart.params['in-reply-to'])
1395 op.records.add('pushkey', {'return': ret}, partid)
1417 op.records.add('pushkey', {'return': ret}, partid)
1396
1418
1397 @parthandler('obsmarkers')
1419 @parthandler('obsmarkers')
1398 def handleobsmarker(op, inpart):
1420 def handleobsmarker(op, inpart):
1399 """add a stream of obsmarkers to the repo"""
1421 """add a stream of obsmarkers to the repo"""
1400 tr = op.gettransaction()
1422 tr = op.gettransaction()
1401 markerdata = inpart.read()
1423 markerdata = inpart.read()
1402 if op.ui.config('experimental', 'obsmarkers-exchange-debug', False):
1424 if op.ui.config('experimental', 'obsmarkers-exchange-debug', False):
1403 op.ui.write(('obsmarker-exchange: %i bytes received\n')
1425 op.ui.write(('obsmarker-exchange: %i bytes received\n')
1404 % len(markerdata))
1426 % len(markerdata))
1405 new = op.repo.obsstore.mergemarkers(tr, markerdata)
1427 new = op.repo.obsstore.mergemarkers(tr, markerdata)
1406 if new:
1428 if new:
1407 op.repo.ui.status(_('%i new obsolescence markers\n') % new)
1429 op.repo.ui.status(_('%i new obsolescence markers\n') % new)
1408 op.records.add('obsmarkers', {'new': new})
1430 op.records.add('obsmarkers', {'new': new})
1409 if op.reply is not None:
1431 if op.reply is not None:
1410 rpart = op.reply.newpart('reply:obsmarkers')
1432 rpart = op.reply.newpart('reply:obsmarkers')
1411 rpart.addparam('in-reply-to', str(inpart.id), mandatory=False)
1433 rpart.addparam('in-reply-to', str(inpart.id), mandatory=False)
1412 rpart.addparam('new', '%i' % new, mandatory=False)
1434 rpart.addparam('new', '%i' % new, mandatory=False)
1413
1435
1414
1436
1415 @parthandler('reply:obsmarkers', ('new', 'in-reply-to'))
1437 @parthandler('reply:obsmarkers', ('new', 'in-reply-to'))
1416 def handleobsmarkerreply(op, inpart):
1438 def handleobsmarkerreply(op, inpart):
1417 """retrieve the result of a pushkey request"""
1439 """retrieve the result of a pushkey request"""
1418 ret = int(inpart.params['new'])
1440 ret = int(inpart.params['new'])
1419 partid = int(inpart.params['in-reply-to'])
1441 partid = int(inpart.params['in-reply-to'])
1420 op.records.add('obsmarkers', {'new': ret}, partid)
1442 op.records.add('obsmarkers', {'new': ret}, partid)
1421
1443
1422 @parthandler('hgtagsfnodes')
1444 @parthandler('hgtagsfnodes')
1423 def handlehgtagsfnodes(op, inpart):
1445 def handlehgtagsfnodes(op, inpart):
1424 """Applies .hgtags fnodes cache entries to the local repo.
1446 """Applies .hgtags fnodes cache entries to the local repo.
1425
1447
1426 Payload is pairs of 20 byte changeset nodes and filenodes.
1448 Payload is pairs of 20 byte changeset nodes and filenodes.
1427 """
1449 """
1428 cache = tags.hgtagsfnodescache(op.repo.unfiltered())
1450 cache = tags.hgtagsfnodescache(op.repo.unfiltered())
1429
1451
1430 count = 0
1452 count = 0
1431 while True:
1453 while True:
1432 node = inpart.read(20)
1454 node = inpart.read(20)
1433 fnode = inpart.read(20)
1455 fnode = inpart.read(20)
1434 if len(node) < 20 or len(fnode) < 20:
1456 if len(node) < 20 or len(fnode) < 20:
1435 op.ui.debug('ignoring incomplete received .hgtags fnodes data\n')
1457 op.ui.debug('ignoring incomplete received .hgtags fnodes data\n')
1436 break
1458 break
1437 cache.setfnode(node, fnode)
1459 cache.setfnode(node, fnode)
1438 count += 1
1460 count += 1
1439
1461
1440 cache.write()
1462 cache.write()
1441 op.ui.debug('applied %i hgtags fnodes cache entries\n' % count)
1463 op.ui.debug('applied %i hgtags fnodes cache entries\n' % count)
@@ -1,1048 +1,1223 b''
1 This test is dedicated to test the bundle2 container format
1 This test is dedicated to test the bundle2 container format
2
2
3 It test multiple existing parts to test different feature of the container. You
3 It test multiple existing parts to test different feature of the container. You
4 probably do not need to touch this test unless you change the binary encoding
4 probably do not need to touch this test unless you change the binary encoding
5 of the bundle2 format itself.
5 of the bundle2 format itself.
6
6
7 Create an extension to test bundle2 API
7 Create an extension to test bundle2 API
8
8
9 $ cat > bundle2.py << EOF
9 $ cat > bundle2.py << EOF
10 > """A small extension to test bundle2 implementation
10 > """A small extension to test bundle2 implementation
11 >
11 >
12 > Current bundle2 implementation is far too limited to be used in any core
12 > Current bundle2 implementation is far too limited to be used in any core
13 > code. We still need to be able to test it while it grow up.
13 > code. We still need to be able to test it while it grow up.
14 > """
14 > """
15 >
15 >
16 > import sys, os
16 > import sys, os
17 > from mercurial import cmdutil
17 > from mercurial import cmdutil
18 > from mercurial import util
18 > from mercurial import util
19 > from mercurial import bundle2
19 > from mercurial import bundle2
20 > from mercurial import scmutil
20 > from mercurial import scmutil
21 > from mercurial import discovery
21 > from mercurial import discovery
22 > from mercurial import changegroup
22 > from mercurial import changegroup
23 > from mercurial import error
23 > from mercurial import error
24 > from mercurial import obsolete
24 > from mercurial import obsolete
25 >
25 >
26 >
26 >
27 > try:
27 > try:
28 > import msvcrt
28 > import msvcrt
29 > msvcrt.setmode(sys.stdin.fileno(), os.O_BINARY)
29 > msvcrt.setmode(sys.stdin.fileno(), os.O_BINARY)
30 > msvcrt.setmode(sys.stdout.fileno(), os.O_BINARY)
30 > msvcrt.setmode(sys.stdout.fileno(), os.O_BINARY)
31 > msvcrt.setmode(sys.stderr.fileno(), os.O_BINARY)
31 > msvcrt.setmode(sys.stderr.fileno(), os.O_BINARY)
32 > except ImportError:
32 > except ImportError:
33 > pass
33 > pass
34 >
34 >
35 > cmdtable = {}
35 > cmdtable = {}
36 > command = cmdutil.command(cmdtable)
36 > command = cmdutil.command(cmdtable)
37 >
37 >
38 > ELEPHANTSSONG = """Patali Dirapata, Cromda Cromda Ripalo, Pata Pata, Ko Ko Ko
38 > ELEPHANTSSONG = """Patali Dirapata, Cromda Cromda Ripalo, Pata Pata, Ko Ko Ko
39 > Bokoro Dipoulito, Rondi Rondi Pepino, Pata Pata, Ko Ko Ko
39 > Bokoro Dipoulito, Rondi Rondi Pepino, Pata Pata, Ko Ko Ko
40 > Emana Karassoli, Loucra Loucra Ponponto, Pata Pata, Ko Ko Ko."""
40 > Emana Karassoli, Loucra Loucra Ponponto, Pata Pata, Ko Ko Ko."""
41 > assert len(ELEPHANTSSONG) == 178 # future test say 178 bytes, trust it.
41 > assert len(ELEPHANTSSONG) == 178 # future test say 178 bytes, trust it.
42 >
42 >
43 > @bundle2.parthandler('test:song')
43 > @bundle2.parthandler('test:song')
44 > def songhandler(op, part):
44 > def songhandler(op, part):
45 > """handle a "test:song" bundle2 part, printing the lyrics on stdin"""
45 > """handle a "test:song" bundle2 part, printing the lyrics on stdin"""
46 > op.ui.write('The choir starts singing:\n')
46 > op.ui.write('The choir starts singing:\n')
47 > verses = 0
47 > verses = 0
48 > for line in part.read().split('\n'):
48 > for line in part.read().split('\n'):
49 > op.ui.write(' %s\n' % line)
49 > op.ui.write(' %s\n' % line)
50 > verses += 1
50 > verses += 1
51 > op.records.add('song', {'verses': verses})
51 > op.records.add('song', {'verses': verses})
52 >
52 >
53 > @bundle2.parthandler('test:ping')
53 > @bundle2.parthandler('test:ping')
54 > def pinghandler(op, part):
54 > def pinghandler(op, part):
55 > op.ui.write('received ping request (id %i)\n' % part.id)
55 > op.ui.write('received ping request (id %i)\n' % part.id)
56 > if op.reply is not None and 'ping-pong' in op.reply.capabilities:
56 > if op.reply is not None and 'ping-pong' in op.reply.capabilities:
57 > op.ui.write_err('replying to ping request (id %i)\n' % part.id)
57 > op.ui.write_err('replying to ping request (id %i)\n' % part.id)
58 > op.reply.newpart('test:pong', [('in-reply-to', str(part.id))],
58 > op.reply.newpart('test:pong', [('in-reply-to', str(part.id))],
59 > mandatory=False)
59 > mandatory=False)
60 >
60 >
61 > @bundle2.parthandler('test:debugreply')
61 > @bundle2.parthandler('test:debugreply')
62 > def debugreply(op, part):
62 > def debugreply(op, part):
63 > """print data about the capacity of the bundle reply"""
63 > """print data about the capacity of the bundle reply"""
64 > if op.reply is None:
64 > if op.reply is None:
65 > op.ui.write('debugreply: no reply\n')
65 > op.ui.write('debugreply: no reply\n')
66 > else:
66 > else:
67 > op.ui.write('debugreply: capabilities:\n')
67 > op.ui.write('debugreply: capabilities:\n')
68 > for cap in sorted(op.reply.capabilities):
68 > for cap in sorted(op.reply.capabilities):
69 > op.ui.write('debugreply: %r\n' % cap)
69 > op.ui.write('debugreply: %r\n' % cap)
70 > for val in op.reply.capabilities[cap]:
70 > for val in op.reply.capabilities[cap]:
71 > op.ui.write('debugreply: %r\n' % val)
71 > op.ui.write('debugreply: %r\n' % val)
72 >
72 >
73 > @command('bundle2',
73 > @command('bundle2',
74 > [('', 'param', [], 'stream level parameter'),
74 > [('', 'param', [], 'stream level parameter'),
75 > ('', 'unknown', False, 'include an unknown mandatory part in the bundle'),
75 > ('', 'unknown', False, 'include an unknown mandatory part in the bundle'),
76 > ('', 'unknownparams', False, 'include an unknown part parameters in the bundle'),
76 > ('', 'unknownparams', False, 'include an unknown part parameters in the bundle'),
77 > ('', 'parts', False, 'include some arbitrary parts to the bundle'),
77 > ('', 'parts', False, 'include some arbitrary parts to the bundle'),
78 > ('', 'reply', False, 'produce a reply bundle'),
78 > ('', 'reply', False, 'produce a reply bundle'),
79 > ('', 'pushrace', False, 'includes a check:head part with unknown nodes'),
79 > ('', 'pushrace', False, 'includes a check:head part with unknown nodes'),
80 > ('', 'genraise', False, 'includes a part that raise an exception during generation'),
80 > ('', 'genraise', False, 'includes a part that raise an exception during generation'),
81 > ('', 'timeout', False, 'emulate a timeout during bundle generation'),
81 > ('', 'timeout', False, 'emulate a timeout during bundle generation'),
82 > ('r', 'rev', [], 'includes those changeset in the bundle'),],
82 > ('r', 'rev', [], 'includes those changeset in the bundle'),
83 > ('', 'compress', '', 'compress the stream'),],
83 > '[OUTPUTFILE]')
84 > '[OUTPUTFILE]')
84 > def cmdbundle2(ui, repo, path=None, **opts):
85 > def cmdbundle2(ui, repo, path=None, **opts):
85 > """write a bundle2 container on standard output"""
86 > """write a bundle2 container on standard output"""
86 > bundler = bundle2.bundle20(ui)
87 > bundler = bundle2.bundle20(ui)
87 > for p in opts['param']:
88 > for p in opts['param']:
88 > p = p.split('=', 1)
89 > p = p.split('=', 1)
89 > try:
90 > try:
90 > bundler.addparam(*p)
91 > bundler.addparam(*p)
91 > except ValueError, exc:
92 > except ValueError, exc:
92 > raise util.Abort('%s' % exc)
93 > raise util.Abort('%s' % exc)
93 >
94 >
95 > if opts['compress']:
96 > bundler.setcompression(opts['compress'])
97 >
94 > if opts['reply']:
98 > if opts['reply']:
95 > capsstring = 'ping-pong\nelephants=babar,celeste\ncity%3D%21=celeste%2Cville'
99 > capsstring = 'ping-pong\nelephants=babar,celeste\ncity%3D%21=celeste%2Cville'
96 > bundler.newpart('replycaps', data=capsstring)
100 > bundler.newpart('replycaps', data=capsstring)
97 >
101 >
98 > if opts['pushrace']:
102 > if opts['pushrace']:
99 > # also serve to test the assignement of data outside of init
103 > # also serve to test the assignement of data outside of init
100 > part = bundler.newpart('check:heads')
104 > part = bundler.newpart('check:heads')
101 > part.data = '01234567890123456789'
105 > part.data = '01234567890123456789'
102 >
106 >
103 > revs = opts['rev']
107 > revs = opts['rev']
104 > if 'rev' in opts:
108 > if 'rev' in opts:
105 > revs = scmutil.revrange(repo, opts['rev'])
109 > revs = scmutil.revrange(repo, opts['rev'])
106 > if revs:
110 > if revs:
107 > # very crude version of a changegroup part creation
111 > # very crude version of a changegroup part creation
108 > bundled = repo.revs('%ld::%ld', revs, revs)
112 > bundled = repo.revs('%ld::%ld', revs, revs)
109 > headmissing = [c.node() for c in repo.set('heads(%ld)', revs)]
113 > headmissing = [c.node() for c in repo.set('heads(%ld)', revs)]
110 > headcommon = [c.node() for c in repo.set('parents(%ld) - %ld', revs, revs)]
114 > headcommon = [c.node() for c in repo.set('parents(%ld) - %ld', revs, revs)]
111 > outgoing = discovery.outgoing(repo.changelog, headcommon, headmissing)
115 > outgoing = discovery.outgoing(repo.changelog, headcommon, headmissing)
112 > cg = changegroup.getlocalchangegroup(repo, 'test:bundle2', outgoing, None)
116 > cg = changegroup.getlocalchangegroup(repo, 'test:bundle2', outgoing, None)
113 > bundler.newpart('changegroup', data=cg.getchunks(),
117 > bundler.newpart('changegroup', data=cg.getchunks(),
114 > mandatory=False)
118 > mandatory=False)
115 >
119 >
116 > if opts['parts']:
120 > if opts['parts']:
117 > bundler.newpart('test:empty', mandatory=False)
121 > bundler.newpart('test:empty', mandatory=False)
118 > # add a second one to make sure we handle multiple parts
122 > # add a second one to make sure we handle multiple parts
119 > bundler.newpart('test:empty', mandatory=False)
123 > bundler.newpart('test:empty', mandatory=False)
120 > bundler.newpart('test:song', data=ELEPHANTSSONG, mandatory=False)
124 > bundler.newpart('test:song', data=ELEPHANTSSONG, mandatory=False)
121 > bundler.newpart('test:debugreply', mandatory=False)
125 > bundler.newpart('test:debugreply', mandatory=False)
122 > mathpart = bundler.newpart('test:math')
126 > mathpart = bundler.newpart('test:math')
123 > mathpart.addparam('pi', '3.14')
127 > mathpart.addparam('pi', '3.14')
124 > mathpart.addparam('e', '2.72')
128 > mathpart.addparam('e', '2.72')
125 > mathpart.addparam('cooking', 'raw', mandatory=False)
129 > mathpart.addparam('cooking', 'raw', mandatory=False)
126 > mathpart.data = '42'
130 > mathpart.data = '42'
127 > mathpart.mandatory = False
131 > mathpart.mandatory = False
128 > # advisory known part with unknown mandatory param
132 > # advisory known part with unknown mandatory param
129 > bundler.newpart('test:song', [('randomparam','')], mandatory=False)
133 > bundler.newpart('test:song', [('randomparam','')], mandatory=False)
130 > if opts['unknown']:
134 > if opts['unknown']:
131 > bundler.newpart('test:unknown', data='some random content')
135 > bundler.newpart('test:unknown', data='some random content')
132 > if opts['unknownparams']:
136 > if opts['unknownparams']:
133 > bundler.newpart('test:song', [('randomparams', '')])
137 > bundler.newpart('test:song', [('randomparams', '')])
134 > if opts['parts']:
138 > if opts['parts']:
135 > bundler.newpart('test:ping', mandatory=False)
139 > bundler.newpart('test:ping', mandatory=False)
136 > if opts['genraise']:
140 > if opts['genraise']:
137 > def genraise():
141 > def genraise():
138 > yield 'first line\n'
142 > yield 'first line\n'
139 > raise RuntimeError('Someone set up us the bomb!')
143 > raise RuntimeError('Someone set up us the bomb!')
140 > bundler.newpart('output', data=genraise(), mandatory=False)
144 > bundler.newpart('output', data=genraise(), mandatory=False)
141 >
145 >
142 > if path is None:
146 > if path is None:
143 > file = sys.stdout
147 > file = sys.stdout
144 > else:
148 > else:
145 > file = open(path, 'wb')
149 > file = open(path, 'wb')
146 >
150 >
147 > if opts['timeout']:
151 > if opts['timeout']:
148 > bundler.newpart('test:song', data=ELEPHANTSSONG, mandatory=False)
152 > bundler.newpart('test:song', data=ELEPHANTSSONG, mandatory=False)
149 > for idx, junk in enumerate(bundler.getchunks()):
153 > for idx, junk in enumerate(bundler.getchunks()):
150 > ui.write('%d chunk\n' % idx)
154 > ui.write('%d chunk\n' % idx)
151 > if idx > 4:
155 > if idx > 4:
152 > # This throws a GeneratorExit inside the generator, which
156 > # This throws a GeneratorExit inside the generator, which
153 > # can cause problems if the exception-recovery code is
157 > # can cause problems if the exception-recovery code is
154 > # too zealous. It's important for this test that the break
158 > # too zealous. It's important for this test that the break
155 > # occur while we're in the middle of a part.
159 > # occur while we're in the middle of a part.
156 > break
160 > break
157 > ui.write('fake timeout complete.\n')
161 > ui.write('fake timeout complete.\n')
158 > return
162 > return
159 > try:
163 > try:
160 > for chunk in bundler.getchunks():
164 > for chunk in bundler.getchunks():
161 > file.write(chunk)
165 > file.write(chunk)
162 > except RuntimeError, exc:
166 > except RuntimeError, exc:
163 > raise util.Abort(exc)
167 > raise util.Abort(exc)
164 >
168 >
165 > @command('unbundle2', [], '')
169 > @command('unbundle2', [], '')
166 > def cmdunbundle2(ui, repo, replypath=None):
170 > def cmdunbundle2(ui, repo, replypath=None):
167 > """process a bundle2 stream from stdin on the current repo"""
171 > """process a bundle2 stream from stdin on the current repo"""
168 > try:
172 > try:
169 > tr = None
173 > tr = None
170 > lock = repo.lock()
174 > lock = repo.lock()
171 > tr = repo.transaction('processbundle')
175 > tr = repo.transaction('processbundle')
172 > try:
176 > try:
173 > unbundler = bundle2.getunbundler(ui, sys.stdin)
177 > unbundler = bundle2.getunbundler(ui, sys.stdin)
174 > op = bundle2.processbundle(repo, unbundler, lambda: tr)
178 > op = bundle2.processbundle(repo, unbundler, lambda: tr)
175 > tr.close()
179 > tr.close()
176 > except error.BundleValueError, exc:
180 > except error.BundleValueError, exc:
177 > raise util.Abort('missing support for %s' % exc)
181 > raise util.Abort('missing support for %s' % exc)
178 > except error.PushRaced, exc:
182 > except error.PushRaced, exc:
179 > raise util.Abort('push race: %s' % exc)
183 > raise util.Abort('push race: %s' % exc)
180 > finally:
184 > finally:
181 > if tr is not None:
185 > if tr is not None:
182 > tr.release()
186 > tr.release()
183 > lock.release()
187 > lock.release()
184 > remains = sys.stdin.read()
188 > remains = sys.stdin.read()
185 > ui.write('%i unread bytes\n' % len(remains))
189 > ui.write('%i unread bytes\n' % len(remains))
186 > if op.records['song']:
190 > if op.records['song']:
187 > totalverses = sum(r['verses'] for r in op.records['song'])
191 > totalverses = sum(r['verses'] for r in op.records['song'])
188 > ui.write('%i total verses sung\n' % totalverses)
192 > ui.write('%i total verses sung\n' % totalverses)
189 > for rec in op.records['changegroup']:
193 > for rec in op.records['changegroup']:
190 > ui.write('addchangegroup return: %i\n' % rec['return'])
194 > ui.write('addchangegroup return: %i\n' % rec['return'])
191 > if op.reply is not None and replypath is not None:
195 > if op.reply is not None and replypath is not None:
192 > file = open(replypath, 'wb')
196 > file = open(replypath, 'wb')
193 > for chunk in op.reply.getchunks():
197 > for chunk in op.reply.getchunks():
194 > file.write(chunk)
198 > file.write(chunk)
195 >
199 >
196 > @command('statbundle2', [], '')
200 > @command('statbundle2', [], '')
197 > def cmdstatbundle2(ui, repo):
201 > def cmdstatbundle2(ui, repo):
198 > """print statistic on the bundle2 container read from stdin"""
202 > """print statistic on the bundle2 container read from stdin"""
199 > unbundler = bundle2.getunbundler(ui, sys.stdin)
203 > unbundler = bundle2.getunbundler(ui, sys.stdin)
200 > try:
204 > try:
201 > params = unbundler.params
205 > params = unbundler.params
202 > except error.BundleValueError, exc:
206 > except error.BundleValueError, exc:
203 > raise util.Abort('unknown parameters: %s' % exc)
207 > raise util.Abort('unknown parameters: %s' % exc)
204 > ui.write('options count: %i\n' % len(params))
208 > ui.write('options count: %i\n' % len(params))
205 > for key in sorted(params):
209 > for key in sorted(params):
206 > ui.write('- %s\n' % key)
210 > ui.write('- %s\n' % key)
207 > value = params[key]
211 > value = params[key]
208 > if value is not None:
212 > if value is not None:
209 > ui.write(' %s\n' % value)
213 > ui.write(' %s\n' % value)
210 > count = 0
214 > count = 0
211 > for p in unbundler.iterparts():
215 > for p in unbundler.iterparts():
212 > count += 1
216 > count += 1
213 > ui.write(' :%s:\n' % p.type)
217 > ui.write(' :%s:\n' % p.type)
214 > ui.write(' mandatory: %i\n' % len(p.mandatoryparams))
218 > ui.write(' mandatory: %i\n' % len(p.mandatoryparams))
215 > ui.write(' advisory: %i\n' % len(p.advisoryparams))
219 > ui.write(' advisory: %i\n' % len(p.advisoryparams))
216 > ui.write(' payload: %i bytes\n' % len(p.read()))
220 > ui.write(' payload: %i bytes\n' % len(p.read()))
217 > ui.write('parts count: %i\n' % count)
221 > ui.write('parts count: %i\n' % count)
218 > EOF
222 > EOF
219 $ cat >> $HGRCPATH << EOF
223 $ cat >> $HGRCPATH << EOF
220 > [extensions]
224 > [extensions]
221 > bundle2=$TESTTMP/bundle2.py
225 > bundle2=$TESTTMP/bundle2.py
222 > [experimental]
226 > [experimental]
223 > bundle2-exp=True
227 > bundle2-exp=True
224 > evolution=createmarkers
228 > evolution=createmarkers
225 > [ui]
229 > [ui]
226 > ssh=python "$TESTDIR/dummyssh"
230 > ssh=python "$TESTDIR/dummyssh"
227 > logtemplate={rev}:{node|short} {phase} {author} {bookmarks} {desc|firstline}
231 > logtemplate={rev}:{node|short} {phase} {author} {bookmarks} {desc|firstline}
228 > [web]
232 > [web]
229 > push_ssl = false
233 > push_ssl = false
230 > allow_push = *
234 > allow_push = *
231 > [phases]
235 > [phases]
232 > publish=False
236 > publish=False
233 > EOF
237 > EOF
234
238
235 The extension requires a repo (currently unused)
239 The extension requires a repo (currently unused)
236
240
237 $ hg init main
241 $ hg init main
238 $ cd main
242 $ cd main
239 $ touch a
243 $ touch a
240 $ hg add a
244 $ hg add a
241 $ hg commit -m 'a'
245 $ hg commit -m 'a'
242
246
243
247
244 Empty bundle
248 Empty bundle
245 =================
249 =================
246
250
247 - no option
251 - no option
248 - no parts
252 - no parts
249
253
250 Test bundling
254 Test bundling
251
255
252 $ hg bundle2 | f --hexdump
256 $ hg bundle2 | f --hexdump
253
257
254 0000: 48 47 32 30 00 00 00 00 00 00 00 00 |HG20........|
258 0000: 48 47 32 30 00 00 00 00 00 00 00 00 |HG20........|
255
259
256 Test timeouts during bundling
260 Test timeouts during bundling
257 $ hg bundle2 --timeout --debug --config devel.bundle2.debug=yes
261 $ hg bundle2 --timeout --debug --config devel.bundle2.debug=yes
258 bundle2-output-bundle: "HG20", 1 parts total
262 bundle2-output-bundle: "HG20", 1 parts total
259 bundle2-output: start emission of HG20 stream
263 bundle2-output: start emission of HG20 stream
260 0 chunk
264 0 chunk
261 bundle2-output: bundle parameter:
265 bundle2-output: bundle parameter:
262 1 chunk
266 1 chunk
263 bundle2-output: start of parts
267 bundle2-output: start of parts
264 bundle2-output: bundle part: "test:song"
268 bundle2-output: bundle part: "test:song"
265 bundle2-output-part: "test:song" (advisory) 178 bytes payload
269 bundle2-output-part: "test:song" (advisory) 178 bytes payload
266 bundle2-output: part 0: "test:song"
270 bundle2-output: part 0: "test:song"
267 bundle2-output: header chunk size: 16
271 bundle2-output: header chunk size: 16
268 2 chunk
272 2 chunk
269 3 chunk
273 3 chunk
270 bundle2-output: payload chunk size: 178
274 bundle2-output: payload chunk size: 178
271 4 chunk
275 4 chunk
272 5 chunk
276 5 chunk
273 bundle2-generatorexit
277 bundle2-generatorexit
274 fake timeout complete.
278 fake timeout complete.
275
279
276 Test unbundling
280 Test unbundling
277
281
278 $ hg bundle2 | hg statbundle2
282 $ hg bundle2 | hg statbundle2
279 options count: 0
283 options count: 0
280 parts count: 0
284 parts count: 0
281
285
282 Test old style bundle are detected and refused
286 Test old style bundle are detected and refused
283
287
284 $ hg bundle --all ../bundle.hg
288 $ hg bundle --all ../bundle.hg
285 1 changesets found
289 1 changesets found
286 $ hg statbundle2 < ../bundle.hg
290 $ hg statbundle2 < ../bundle.hg
287 abort: unknown bundle version 10
291 abort: unknown bundle version 10
288 [255]
292 [255]
289
293
290 Test parameters
294 Test parameters
291 =================
295 =================
292
296
293 - some options
297 - some options
294 - no parts
298 - no parts
295
299
296 advisory parameters, no value
300 advisory parameters, no value
297 -------------------------------
301 -------------------------------
298
302
299 Simplest possible parameters form
303 Simplest possible parameters form
300
304
301 Test generation simple option
305 Test generation simple option
302
306
303 $ hg bundle2 --param 'caution' | f --hexdump
307 $ hg bundle2 --param 'caution' | f --hexdump
304
308
305 0000: 48 47 32 30 00 00 00 07 63 61 75 74 69 6f 6e 00 |HG20....caution.|
309 0000: 48 47 32 30 00 00 00 07 63 61 75 74 69 6f 6e 00 |HG20....caution.|
306 0010: 00 00 00 |...|
310 0010: 00 00 00 |...|
307
311
308 Test unbundling
312 Test unbundling
309
313
310 $ hg bundle2 --param 'caution' | hg statbundle2
314 $ hg bundle2 --param 'caution' | hg statbundle2
311 options count: 1
315 options count: 1
312 - caution
316 - caution
313 parts count: 0
317 parts count: 0
314
318
315 Test generation multiple option
319 Test generation multiple option
316
320
317 $ hg bundle2 --param 'caution' --param 'meal' | f --hexdump
321 $ hg bundle2 --param 'caution' --param 'meal' | f --hexdump
318
322
319 0000: 48 47 32 30 00 00 00 0c 63 61 75 74 69 6f 6e 20 |HG20....caution |
323 0000: 48 47 32 30 00 00 00 0c 63 61 75 74 69 6f 6e 20 |HG20....caution |
320 0010: 6d 65 61 6c 00 00 00 00 |meal....|
324 0010: 6d 65 61 6c 00 00 00 00 |meal....|
321
325
322 Test unbundling
326 Test unbundling
323
327
324 $ hg bundle2 --param 'caution' --param 'meal' | hg statbundle2
328 $ hg bundle2 --param 'caution' --param 'meal' | hg statbundle2
325 options count: 2
329 options count: 2
326 - caution
330 - caution
327 - meal
331 - meal
328 parts count: 0
332 parts count: 0
329
333
330 advisory parameters, with value
334 advisory parameters, with value
331 -------------------------------
335 -------------------------------
332
336
333 Test generation
337 Test generation
334
338
335 $ hg bundle2 --param 'caution' --param 'meal=vegan' --param 'elephants' | f --hexdump
339 $ hg bundle2 --param 'caution' --param 'meal=vegan' --param 'elephants' | f --hexdump
336
340
337 0000: 48 47 32 30 00 00 00 1c 63 61 75 74 69 6f 6e 20 |HG20....caution |
341 0000: 48 47 32 30 00 00 00 1c 63 61 75 74 69 6f 6e 20 |HG20....caution |
338 0010: 6d 65 61 6c 3d 76 65 67 61 6e 20 65 6c 65 70 68 |meal=vegan eleph|
342 0010: 6d 65 61 6c 3d 76 65 67 61 6e 20 65 6c 65 70 68 |meal=vegan eleph|
339 0020: 61 6e 74 73 00 00 00 00 |ants....|
343 0020: 61 6e 74 73 00 00 00 00 |ants....|
340
344
341 Test unbundling
345 Test unbundling
342
346
343 $ hg bundle2 --param 'caution' --param 'meal=vegan' --param 'elephants' | hg statbundle2
347 $ hg bundle2 --param 'caution' --param 'meal=vegan' --param 'elephants' | hg statbundle2
344 options count: 3
348 options count: 3
345 - caution
349 - caution
346 - elephants
350 - elephants
347 - meal
351 - meal
348 vegan
352 vegan
349 parts count: 0
353 parts count: 0
350
354
351 parameter with special char in value
355 parameter with special char in value
352 ---------------------------------------------------
356 ---------------------------------------------------
353
357
354 Test generation
358 Test generation
355
359
356 $ hg bundle2 --param 'e|! 7/=babar%#==tutu' --param simple | f --hexdump
360 $ hg bundle2 --param 'e|! 7/=babar%#==tutu' --param simple | f --hexdump
357
361
358 0000: 48 47 32 30 00 00 00 29 65 25 37 43 25 32 31 25 |HG20...)e%7C%21%|
362 0000: 48 47 32 30 00 00 00 29 65 25 37 43 25 32 31 25 |HG20...)e%7C%21%|
359 0010: 32 30 37 2f 3d 62 61 62 61 72 25 32 35 25 32 33 |207/=babar%25%23|
363 0010: 32 30 37 2f 3d 62 61 62 61 72 25 32 35 25 32 33 |207/=babar%25%23|
360 0020: 25 33 44 25 33 44 74 75 74 75 20 73 69 6d 70 6c |%3D%3Dtutu simpl|
364 0020: 25 33 44 25 33 44 74 75 74 75 20 73 69 6d 70 6c |%3D%3Dtutu simpl|
361 0030: 65 00 00 00 00 |e....|
365 0030: 65 00 00 00 00 |e....|
362
366
363 Test unbundling
367 Test unbundling
364
368
365 $ hg bundle2 --param 'e|! 7/=babar%#==tutu' --param simple | hg statbundle2
369 $ hg bundle2 --param 'e|! 7/=babar%#==tutu' --param simple | hg statbundle2
366 options count: 2
370 options count: 2
367 - e|! 7/
371 - e|! 7/
368 babar%#==tutu
372 babar%#==tutu
369 - simple
373 - simple
370 parts count: 0
374 parts count: 0
371
375
372 Test unknown mandatory option
376 Test unknown mandatory option
373 ---------------------------------------------------
377 ---------------------------------------------------
374
378
375 $ hg bundle2 --param 'Gravity' | hg statbundle2
379 $ hg bundle2 --param 'Gravity' | hg statbundle2
376 abort: unknown parameters: Stream Parameter - Gravity
380 abort: unknown parameters: Stream Parameter - Gravity
377 [255]
381 [255]
378
382
379 Test debug output
383 Test debug output
380 ---------------------------------------------------
384 ---------------------------------------------------
381
385
382 bundling debug
386 bundling debug
383
387
384 $ hg bundle2 --debug --param 'e|! 7/=babar%#==tutu' --param simple ../out.hg2 --config progress.debug=true --config devel.bundle2.debug=true
388 $ hg bundle2 --debug --param 'e|! 7/=babar%#==tutu' --param simple ../out.hg2 --config progress.debug=true --config devel.bundle2.debug=true
385 bundle2-output-bundle: "HG20", (2 params) 0 parts total
389 bundle2-output-bundle: "HG20", (2 params) 0 parts total
386 bundle2-output: start emission of HG20 stream
390 bundle2-output: start emission of HG20 stream
387 bundle2-output: bundle parameter: e%7C%21%207/=babar%25%23%3D%3Dtutu simple
391 bundle2-output: bundle parameter: e%7C%21%207/=babar%25%23%3D%3Dtutu simple
388 bundle2-output: start of parts
392 bundle2-output: start of parts
389 bundle2-output: end of bundle
393 bundle2-output: end of bundle
390
394
391 file content is ok
395 file content is ok
392
396
393 $ f --hexdump ../out.hg2
397 $ f --hexdump ../out.hg2
394 ../out.hg2:
398 ../out.hg2:
395 0000: 48 47 32 30 00 00 00 29 65 25 37 43 25 32 31 25 |HG20...)e%7C%21%|
399 0000: 48 47 32 30 00 00 00 29 65 25 37 43 25 32 31 25 |HG20...)e%7C%21%|
396 0010: 32 30 37 2f 3d 62 61 62 61 72 25 32 35 25 32 33 |207/=babar%25%23|
400 0010: 32 30 37 2f 3d 62 61 62 61 72 25 32 35 25 32 33 |207/=babar%25%23|
397 0020: 25 33 44 25 33 44 74 75 74 75 20 73 69 6d 70 6c |%3D%3Dtutu simpl|
401 0020: 25 33 44 25 33 44 74 75 74 75 20 73 69 6d 70 6c |%3D%3Dtutu simpl|
398 0030: 65 00 00 00 00 |e....|
402 0030: 65 00 00 00 00 |e....|
399
403
400 unbundling debug
404 unbundling debug
401
405
402 $ hg statbundle2 --debug --config progress.debug=true --config devel.bundle2.debug=true < ../out.hg2
406 $ hg statbundle2 --debug --config progress.debug=true --config devel.bundle2.debug=true < ../out.hg2
403 bundle2-input: start processing of HG20 stream
407 bundle2-input: start processing of HG20 stream
404 bundle2-input: reading bundle2 stream parameters
408 bundle2-input: reading bundle2 stream parameters
405 bundle2-input: ignoring unknown parameter 'e|! 7/'
409 bundle2-input: ignoring unknown parameter 'e|! 7/'
406 bundle2-input: ignoring unknown parameter 'simple'
410 bundle2-input: ignoring unknown parameter 'simple'
407 options count: 2
411 options count: 2
408 - e|! 7/
412 - e|! 7/
409 babar%#==tutu
413 babar%#==tutu
410 - simple
414 - simple
411 bundle2-input: start extraction of bundle2 parts
415 bundle2-input: start extraction of bundle2 parts
412 bundle2-input: part header size: 0
416 bundle2-input: part header size: 0
413 bundle2-input: end of bundle2 stream
417 bundle2-input: end of bundle2 stream
414 parts count: 0
418 parts count: 0
415
419
416
420
417 Test buggy input
421 Test buggy input
418 ---------------------------------------------------
422 ---------------------------------------------------
419
423
420 empty parameter name
424 empty parameter name
421
425
422 $ hg bundle2 --param '' --quiet
426 $ hg bundle2 --param '' --quiet
423 abort: empty parameter name
427 abort: empty parameter name
424 [255]
428 [255]
425
429
426 bad parameter name
430 bad parameter name
427
431
428 $ hg bundle2 --param 42babar
432 $ hg bundle2 --param 42babar
429 abort: non letter first character: '42babar'
433 abort: non letter first character: '42babar'
430 [255]
434 [255]
431
435
432
436
433 Test part
437 Test part
434 =================
438 =================
435
439
436 $ hg bundle2 --parts ../parts.hg2 --debug --config progress.debug=true --config devel.bundle2.debug=true
440 $ hg bundle2 --parts ../parts.hg2 --debug --config progress.debug=true --config devel.bundle2.debug=true
437 bundle2-output-bundle: "HG20", 7 parts total
441 bundle2-output-bundle: "HG20", 7 parts total
438 bundle2-output: start emission of HG20 stream
442 bundle2-output: start emission of HG20 stream
439 bundle2-output: bundle parameter:
443 bundle2-output: bundle parameter:
440 bundle2-output: start of parts
444 bundle2-output: start of parts
441 bundle2-output: bundle part: "test:empty"
445 bundle2-output: bundle part: "test:empty"
442 bundle2-output-part: "test:empty" (advisory) empty payload
446 bundle2-output-part: "test:empty" (advisory) empty payload
443 bundle2-output: part 0: "test:empty"
447 bundle2-output: part 0: "test:empty"
444 bundle2-output: header chunk size: 17
448 bundle2-output: header chunk size: 17
445 bundle2-output: closing payload chunk
449 bundle2-output: closing payload chunk
446 bundle2-output: bundle part: "test:empty"
450 bundle2-output: bundle part: "test:empty"
447 bundle2-output-part: "test:empty" (advisory) empty payload
451 bundle2-output-part: "test:empty" (advisory) empty payload
448 bundle2-output: part 1: "test:empty"
452 bundle2-output: part 1: "test:empty"
449 bundle2-output: header chunk size: 17
453 bundle2-output: header chunk size: 17
450 bundle2-output: closing payload chunk
454 bundle2-output: closing payload chunk
451 bundle2-output: bundle part: "test:song"
455 bundle2-output: bundle part: "test:song"
452 bundle2-output-part: "test:song" (advisory) 178 bytes payload
456 bundle2-output-part: "test:song" (advisory) 178 bytes payload
453 bundle2-output: part 2: "test:song"
457 bundle2-output: part 2: "test:song"
454 bundle2-output: header chunk size: 16
458 bundle2-output: header chunk size: 16
455 bundle2-output: payload chunk size: 178
459 bundle2-output: payload chunk size: 178
456 bundle2-output: closing payload chunk
460 bundle2-output: closing payload chunk
457 bundle2-output: bundle part: "test:debugreply"
461 bundle2-output: bundle part: "test:debugreply"
458 bundle2-output-part: "test:debugreply" (advisory) empty payload
462 bundle2-output-part: "test:debugreply" (advisory) empty payload
459 bundle2-output: part 3: "test:debugreply"
463 bundle2-output: part 3: "test:debugreply"
460 bundle2-output: header chunk size: 22
464 bundle2-output: header chunk size: 22
461 bundle2-output: closing payload chunk
465 bundle2-output: closing payload chunk
462 bundle2-output: bundle part: "test:math"
466 bundle2-output: bundle part: "test:math"
463 bundle2-output-part: "test:math" (advisory) (params: 2 mandatory 2 advisory) 2 bytes payload
467 bundle2-output-part: "test:math" (advisory) (params: 2 mandatory 2 advisory) 2 bytes payload
464 bundle2-output: part 4: "test:math"
468 bundle2-output: part 4: "test:math"
465 bundle2-output: header chunk size: 43
469 bundle2-output: header chunk size: 43
466 bundle2-output: payload chunk size: 2
470 bundle2-output: payload chunk size: 2
467 bundle2-output: closing payload chunk
471 bundle2-output: closing payload chunk
468 bundle2-output: bundle part: "test:song"
472 bundle2-output: bundle part: "test:song"
469 bundle2-output-part: "test:song" (advisory) (params: 1 mandatory) empty payload
473 bundle2-output-part: "test:song" (advisory) (params: 1 mandatory) empty payload
470 bundle2-output: part 5: "test:song"
474 bundle2-output: part 5: "test:song"
471 bundle2-output: header chunk size: 29
475 bundle2-output: header chunk size: 29
472 bundle2-output: closing payload chunk
476 bundle2-output: closing payload chunk
473 bundle2-output: bundle part: "test:ping"
477 bundle2-output: bundle part: "test:ping"
474 bundle2-output-part: "test:ping" (advisory) empty payload
478 bundle2-output-part: "test:ping" (advisory) empty payload
475 bundle2-output: part 6: "test:ping"
479 bundle2-output: part 6: "test:ping"
476 bundle2-output: header chunk size: 16
480 bundle2-output: header chunk size: 16
477 bundle2-output: closing payload chunk
481 bundle2-output: closing payload chunk
478 bundle2-output: end of bundle
482 bundle2-output: end of bundle
479
483
480 $ f --hexdump ../parts.hg2
484 $ f --hexdump ../parts.hg2
481 ../parts.hg2:
485 ../parts.hg2:
482 0000: 48 47 32 30 00 00 00 00 00 00 00 11 0a 74 65 73 |HG20.........tes|
486 0000: 48 47 32 30 00 00 00 00 00 00 00 11 0a 74 65 73 |HG20.........tes|
483 0010: 74 3a 65 6d 70 74 79 00 00 00 00 00 00 00 00 00 |t:empty.........|
487 0010: 74 3a 65 6d 70 74 79 00 00 00 00 00 00 00 00 00 |t:empty.........|
484 0020: 00 00 00 00 11 0a 74 65 73 74 3a 65 6d 70 74 79 |......test:empty|
488 0020: 00 00 00 00 11 0a 74 65 73 74 3a 65 6d 70 74 79 |......test:empty|
485 0030: 00 00 00 01 00 00 00 00 00 00 00 00 00 10 09 74 |...............t|
489 0030: 00 00 00 01 00 00 00 00 00 00 00 00 00 10 09 74 |...............t|
486 0040: 65 73 74 3a 73 6f 6e 67 00 00 00 02 00 00 00 00 |est:song........|
490 0040: 65 73 74 3a 73 6f 6e 67 00 00 00 02 00 00 00 00 |est:song........|
487 0050: 00 b2 50 61 74 61 6c 69 20 44 69 72 61 70 61 74 |..Patali Dirapat|
491 0050: 00 b2 50 61 74 61 6c 69 20 44 69 72 61 70 61 74 |..Patali Dirapat|
488 0060: 61 2c 20 43 72 6f 6d 64 61 20 43 72 6f 6d 64 61 |a, Cromda Cromda|
492 0060: 61 2c 20 43 72 6f 6d 64 61 20 43 72 6f 6d 64 61 |a, Cromda Cromda|
489 0070: 20 52 69 70 61 6c 6f 2c 20 50 61 74 61 20 50 61 | Ripalo, Pata Pa|
493 0070: 20 52 69 70 61 6c 6f 2c 20 50 61 74 61 20 50 61 | Ripalo, Pata Pa|
490 0080: 74 61 2c 20 4b 6f 20 4b 6f 20 4b 6f 0a 42 6f 6b |ta, Ko Ko Ko.Bok|
494 0080: 74 61 2c 20 4b 6f 20 4b 6f 20 4b 6f 0a 42 6f 6b |ta, Ko Ko Ko.Bok|
491 0090: 6f 72 6f 20 44 69 70 6f 75 6c 69 74 6f 2c 20 52 |oro Dipoulito, R|
495 0090: 6f 72 6f 20 44 69 70 6f 75 6c 69 74 6f 2c 20 52 |oro Dipoulito, R|
492 00a0: 6f 6e 64 69 20 52 6f 6e 64 69 20 50 65 70 69 6e |ondi Rondi Pepin|
496 00a0: 6f 6e 64 69 20 52 6f 6e 64 69 20 50 65 70 69 6e |ondi Rondi Pepin|
493 00b0: 6f 2c 20 50 61 74 61 20 50 61 74 61 2c 20 4b 6f |o, Pata Pata, Ko|
497 00b0: 6f 2c 20 50 61 74 61 20 50 61 74 61 2c 20 4b 6f |o, Pata Pata, Ko|
494 00c0: 20 4b 6f 20 4b 6f 0a 45 6d 61 6e 61 20 4b 61 72 | Ko Ko.Emana Kar|
498 00c0: 20 4b 6f 20 4b 6f 0a 45 6d 61 6e 61 20 4b 61 72 | Ko Ko.Emana Kar|
495 00d0: 61 73 73 6f 6c 69 2c 20 4c 6f 75 63 72 61 20 4c |assoli, Loucra L|
499 00d0: 61 73 73 6f 6c 69 2c 20 4c 6f 75 63 72 61 20 4c |assoli, Loucra L|
496 00e0: 6f 75 63 72 61 20 50 6f 6e 70 6f 6e 74 6f 2c 20 |oucra Ponponto, |
500 00e0: 6f 75 63 72 61 20 50 6f 6e 70 6f 6e 74 6f 2c 20 |oucra Ponponto, |
497 00f0: 50 61 74 61 20 50 61 74 61 2c 20 4b 6f 20 4b 6f |Pata Pata, Ko Ko|
501 00f0: 50 61 74 61 20 50 61 74 61 2c 20 4b 6f 20 4b 6f |Pata Pata, Ko Ko|
498 0100: 20 4b 6f 2e 00 00 00 00 00 00 00 16 0f 74 65 73 | Ko..........tes|
502 0100: 20 4b 6f 2e 00 00 00 00 00 00 00 16 0f 74 65 73 | Ko..........tes|
499 0110: 74 3a 64 65 62 75 67 72 65 70 6c 79 00 00 00 03 |t:debugreply....|
503 0110: 74 3a 64 65 62 75 67 72 65 70 6c 79 00 00 00 03 |t:debugreply....|
500 0120: 00 00 00 00 00 00 00 00 00 2b 09 74 65 73 74 3a |.........+.test:|
504 0120: 00 00 00 00 00 00 00 00 00 2b 09 74 65 73 74 3a |.........+.test:|
501 0130: 6d 61 74 68 00 00 00 04 02 01 02 04 01 04 07 03 |math............|
505 0130: 6d 61 74 68 00 00 00 04 02 01 02 04 01 04 07 03 |math............|
502 0140: 70 69 33 2e 31 34 65 32 2e 37 32 63 6f 6f 6b 69 |pi3.14e2.72cooki|
506 0140: 70 69 33 2e 31 34 65 32 2e 37 32 63 6f 6f 6b 69 |pi3.14e2.72cooki|
503 0150: 6e 67 72 61 77 00 00 00 02 34 32 00 00 00 00 00 |ngraw....42.....|
507 0150: 6e 67 72 61 77 00 00 00 02 34 32 00 00 00 00 00 |ngraw....42.....|
504 0160: 00 00 1d 09 74 65 73 74 3a 73 6f 6e 67 00 00 00 |....test:song...|
508 0160: 00 00 1d 09 74 65 73 74 3a 73 6f 6e 67 00 00 00 |....test:song...|
505 0170: 05 01 00 0b 00 72 61 6e 64 6f 6d 70 61 72 61 6d |.....randomparam|
509 0170: 05 01 00 0b 00 72 61 6e 64 6f 6d 70 61 72 61 6d |.....randomparam|
506 0180: 00 00 00 00 00 00 00 10 09 74 65 73 74 3a 70 69 |.........test:pi|
510 0180: 00 00 00 00 00 00 00 10 09 74 65 73 74 3a 70 69 |.........test:pi|
507 0190: 6e 67 00 00 00 06 00 00 00 00 00 00 00 00 00 00 |ng..............|
511 0190: 6e 67 00 00 00 06 00 00 00 00 00 00 00 00 00 00 |ng..............|
508
512
509
513
510 $ hg statbundle2 < ../parts.hg2
514 $ hg statbundle2 < ../parts.hg2
511 options count: 0
515 options count: 0
512 :test:empty:
516 :test:empty:
513 mandatory: 0
517 mandatory: 0
514 advisory: 0
518 advisory: 0
515 payload: 0 bytes
519 payload: 0 bytes
516 :test:empty:
520 :test:empty:
517 mandatory: 0
521 mandatory: 0
518 advisory: 0
522 advisory: 0
519 payload: 0 bytes
523 payload: 0 bytes
520 :test:song:
524 :test:song:
521 mandatory: 0
525 mandatory: 0
522 advisory: 0
526 advisory: 0
523 payload: 178 bytes
527 payload: 178 bytes
524 :test:debugreply:
528 :test:debugreply:
525 mandatory: 0
529 mandatory: 0
526 advisory: 0
530 advisory: 0
527 payload: 0 bytes
531 payload: 0 bytes
528 :test:math:
532 :test:math:
529 mandatory: 2
533 mandatory: 2
530 advisory: 1
534 advisory: 1
531 payload: 2 bytes
535 payload: 2 bytes
532 :test:song:
536 :test:song:
533 mandatory: 1
537 mandatory: 1
534 advisory: 0
538 advisory: 0
535 payload: 0 bytes
539 payload: 0 bytes
536 :test:ping:
540 :test:ping:
537 mandatory: 0
541 mandatory: 0
538 advisory: 0
542 advisory: 0
539 payload: 0 bytes
543 payload: 0 bytes
540 parts count: 7
544 parts count: 7
541
545
542 $ hg statbundle2 --debug --config progress.debug=true --config devel.bundle2.debug=true < ../parts.hg2
546 $ hg statbundle2 --debug --config progress.debug=true --config devel.bundle2.debug=true < ../parts.hg2
543 bundle2-input: start processing of HG20 stream
547 bundle2-input: start processing of HG20 stream
544 bundle2-input: reading bundle2 stream parameters
548 bundle2-input: reading bundle2 stream parameters
545 options count: 0
549 options count: 0
546 bundle2-input: start extraction of bundle2 parts
550 bundle2-input: start extraction of bundle2 parts
547 bundle2-input: part header size: 17
551 bundle2-input: part header size: 17
548 bundle2-input: part type: "test:empty"
552 bundle2-input: part type: "test:empty"
549 bundle2-input: part id: "0"
553 bundle2-input: part id: "0"
550 bundle2-input: part parameters: 0
554 bundle2-input: part parameters: 0
551 :test:empty:
555 :test:empty:
552 mandatory: 0
556 mandatory: 0
553 advisory: 0
557 advisory: 0
554 bundle2-input: payload chunk size: 0
558 bundle2-input: payload chunk size: 0
555 payload: 0 bytes
559 payload: 0 bytes
556 bundle2-input: part header size: 17
560 bundle2-input: part header size: 17
557 bundle2-input: part type: "test:empty"
561 bundle2-input: part type: "test:empty"
558 bundle2-input: part id: "1"
562 bundle2-input: part id: "1"
559 bundle2-input: part parameters: 0
563 bundle2-input: part parameters: 0
560 :test:empty:
564 :test:empty:
561 mandatory: 0
565 mandatory: 0
562 advisory: 0
566 advisory: 0
563 bundle2-input: payload chunk size: 0
567 bundle2-input: payload chunk size: 0
564 payload: 0 bytes
568 payload: 0 bytes
565 bundle2-input: part header size: 16
569 bundle2-input: part header size: 16
566 bundle2-input: part type: "test:song"
570 bundle2-input: part type: "test:song"
567 bundle2-input: part id: "2"
571 bundle2-input: part id: "2"
568 bundle2-input: part parameters: 0
572 bundle2-input: part parameters: 0
569 :test:song:
573 :test:song:
570 mandatory: 0
574 mandatory: 0
571 advisory: 0
575 advisory: 0
572 bundle2-input: payload chunk size: 178
576 bundle2-input: payload chunk size: 178
573 bundle2-input: payload chunk size: 0
577 bundle2-input: payload chunk size: 0
574 bundle2-input-part: total payload size 178
578 bundle2-input-part: total payload size 178
575 payload: 178 bytes
579 payload: 178 bytes
576 bundle2-input: part header size: 22
580 bundle2-input: part header size: 22
577 bundle2-input: part type: "test:debugreply"
581 bundle2-input: part type: "test:debugreply"
578 bundle2-input: part id: "3"
582 bundle2-input: part id: "3"
579 bundle2-input: part parameters: 0
583 bundle2-input: part parameters: 0
580 :test:debugreply:
584 :test:debugreply:
581 mandatory: 0
585 mandatory: 0
582 advisory: 0
586 advisory: 0
583 bundle2-input: payload chunk size: 0
587 bundle2-input: payload chunk size: 0
584 payload: 0 bytes
588 payload: 0 bytes
585 bundle2-input: part header size: 43
589 bundle2-input: part header size: 43
586 bundle2-input: part type: "test:math"
590 bundle2-input: part type: "test:math"
587 bundle2-input: part id: "4"
591 bundle2-input: part id: "4"
588 bundle2-input: part parameters: 3
592 bundle2-input: part parameters: 3
589 :test:math:
593 :test:math:
590 mandatory: 2
594 mandatory: 2
591 advisory: 1
595 advisory: 1
592 bundle2-input: payload chunk size: 2
596 bundle2-input: payload chunk size: 2
593 bundle2-input: payload chunk size: 0
597 bundle2-input: payload chunk size: 0
594 bundle2-input-part: total payload size 2
598 bundle2-input-part: total payload size 2
595 payload: 2 bytes
599 payload: 2 bytes
596 bundle2-input: part header size: 29
600 bundle2-input: part header size: 29
597 bundle2-input: part type: "test:song"
601 bundle2-input: part type: "test:song"
598 bundle2-input: part id: "5"
602 bundle2-input: part id: "5"
599 bundle2-input: part parameters: 1
603 bundle2-input: part parameters: 1
600 :test:song:
604 :test:song:
601 mandatory: 1
605 mandatory: 1
602 advisory: 0
606 advisory: 0
603 bundle2-input: payload chunk size: 0
607 bundle2-input: payload chunk size: 0
604 payload: 0 bytes
608 payload: 0 bytes
605 bundle2-input: part header size: 16
609 bundle2-input: part header size: 16
606 bundle2-input: part type: "test:ping"
610 bundle2-input: part type: "test:ping"
607 bundle2-input: part id: "6"
611 bundle2-input: part id: "6"
608 bundle2-input: part parameters: 0
612 bundle2-input: part parameters: 0
609 :test:ping:
613 :test:ping:
610 mandatory: 0
614 mandatory: 0
611 advisory: 0
615 advisory: 0
612 bundle2-input: payload chunk size: 0
616 bundle2-input: payload chunk size: 0
613 payload: 0 bytes
617 payload: 0 bytes
614 bundle2-input: part header size: 0
618 bundle2-input: part header size: 0
615 bundle2-input: end of bundle2 stream
619 bundle2-input: end of bundle2 stream
616 parts count: 7
620 parts count: 7
617
621
618 Test actual unbundling of test part
622 Test actual unbundling of test part
619 =======================================
623 =======================================
620
624
621 Process the bundle
625 Process the bundle
622
626
623 $ hg unbundle2 --debug --config progress.debug=true --config devel.bundle2.debug=true < ../parts.hg2
627 $ hg unbundle2 --debug --config progress.debug=true --config devel.bundle2.debug=true < ../parts.hg2
624 bundle2-input: start processing of HG20 stream
628 bundle2-input: start processing of HG20 stream
625 bundle2-input: reading bundle2 stream parameters
629 bundle2-input: reading bundle2 stream parameters
626 bundle2-input-bundle: with-transaction
630 bundle2-input-bundle: with-transaction
627 bundle2-input: start extraction of bundle2 parts
631 bundle2-input: start extraction of bundle2 parts
628 bundle2-input: part header size: 17
632 bundle2-input: part header size: 17
629 bundle2-input: part type: "test:empty"
633 bundle2-input: part type: "test:empty"
630 bundle2-input: part id: "0"
634 bundle2-input: part id: "0"
631 bundle2-input: part parameters: 0
635 bundle2-input: part parameters: 0
632 bundle2-input: ignoring unsupported advisory part test:empty
636 bundle2-input: ignoring unsupported advisory part test:empty
633 bundle2-input-part: "test:empty" (advisory) unsupported-type
637 bundle2-input-part: "test:empty" (advisory) unsupported-type
634 bundle2-input: payload chunk size: 0
638 bundle2-input: payload chunk size: 0
635 bundle2-input: part header size: 17
639 bundle2-input: part header size: 17
636 bundle2-input: part type: "test:empty"
640 bundle2-input: part type: "test:empty"
637 bundle2-input: part id: "1"
641 bundle2-input: part id: "1"
638 bundle2-input: part parameters: 0
642 bundle2-input: part parameters: 0
639 bundle2-input: ignoring unsupported advisory part test:empty
643 bundle2-input: ignoring unsupported advisory part test:empty
640 bundle2-input-part: "test:empty" (advisory) unsupported-type
644 bundle2-input-part: "test:empty" (advisory) unsupported-type
641 bundle2-input: payload chunk size: 0
645 bundle2-input: payload chunk size: 0
642 bundle2-input: part header size: 16
646 bundle2-input: part header size: 16
643 bundle2-input: part type: "test:song"
647 bundle2-input: part type: "test:song"
644 bundle2-input: part id: "2"
648 bundle2-input: part id: "2"
645 bundle2-input: part parameters: 0
649 bundle2-input: part parameters: 0
646 bundle2-input: found a handler for part 'test:song'
650 bundle2-input: found a handler for part 'test:song'
647 bundle2-input-part: "test:song" (advisory) supported
651 bundle2-input-part: "test:song" (advisory) supported
648 The choir starts singing:
652 The choir starts singing:
649 bundle2-input: payload chunk size: 178
653 bundle2-input: payload chunk size: 178
650 bundle2-input: payload chunk size: 0
654 bundle2-input: payload chunk size: 0
651 bundle2-input-part: total payload size 178
655 bundle2-input-part: total payload size 178
652 Patali Dirapata, Cromda Cromda Ripalo, Pata Pata, Ko Ko Ko
656 Patali Dirapata, Cromda Cromda Ripalo, Pata Pata, Ko Ko Ko
653 Bokoro Dipoulito, Rondi Rondi Pepino, Pata Pata, Ko Ko Ko
657 Bokoro Dipoulito, Rondi Rondi Pepino, Pata Pata, Ko Ko Ko
654 Emana Karassoli, Loucra Loucra Ponponto, Pata Pata, Ko Ko Ko.
658 Emana Karassoli, Loucra Loucra Ponponto, Pata Pata, Ko Ko Ko.
655 bundle2-input: part header size: 22
659 bundle2-input: part header size: 22
656 bundle2-input: part type: "test:debugreply"
660 bundle2-input: part type: "test:debugreply"
657 bundle2-input: part id: "3"
661 bundle2-input: part id: "3"
658 bundle2-input: part parameters: 0
662 bundle2-input: part parameters: 0
659 bundle2-input: found a handler for part 'test:debugreply'
663 bundle2-input: found a handler for part 'test:debugreply'
660 bundle2-input-part: "test:debugreply" (advisory) supported
664 bundle2-input-part: "test:debugreply" (advisory) supported
661 debugreply: no reply
665 debugreply: no reply
662 bundle2-input: payload chunk size: 0
666 bundle2-input: payload chunk size: 0
663 bundle2-input: part header size: 43
667 bundle2-input: part header size: 43
664 bundle2-input: part type: "test:math"
668 bundle2-input: part type: "test:math"
665 bundle2-input: part id: "4"
669 bundle2-input: part id: "4"
666 bundle2-input: part parameters: 3
670 bundle2-input: part parameters: 3
667 bundle2-input: ignoring unsupported advisory part test:math
671 bundle2-input: ignoring unsupported advisory part test:math
668 bundle2-input-part: "test:math" (advisory) (params: 2 mandatory 2 advisory) unsupported-type
672 bundle2-input-part: "test:math" (advisory) (params: 2 mandatory 2 advisory) unsupported-type
669 bundle2-input: payload chunk size: 2
673 bundle2-input: payload chunk size: 2
670 bundle2-input: payload chunk size: 0
674 bundle2-input: payload chunk size: 0
671 bundle2-input-part: total payload size 2
675 bundle2-input-part: total payload size 2
672 bundle2-input: part header size: 29
676 bundle2-input: part header size: 29
673 bundle2-input: part type: "test:song"
677 bundle2-input: part type: "test:song"
674 bundle2-input: part id: "5"
678 bundle2-input: part id: "5"
675 bundle2-input: part parameters: 1
679 bundle2-input: part parameters: 1
676 bundle2-input: found a handler for part 'test:song'
680 bundle2-input: found a handler for part 'test:song'
677 bundle2-input: ignoring unsupported advisory part test:song - randomparam
681 bundle2-input: ignoring unsupported advisory part test:song - randomparam
678 bundle2-input-part: "test:song" (advisory) (params: 1 mandatory) unsupported-params (['randomparam'])
682 bundle2-input-part: "test:song" (advisory) (params: 1 mandatory) unsupported-params (['randomparam'])
679 bundle2-input: payload chunk size: 0
683 bundle2-input: payload chunk size: 0
680 bundle2-input: part header size: 16
684 bundle2-input: part header size: 16
681 bundle2-input: part type: "test:ping"
685 bundle2-input: part type: "test:ping"
682 bundle2-input: part id: "6"
686 bundle2-input: part id: "6"
683 bundle2-input: part parameters: 0
687 bundle2-input: part parameters: 0
684 bundle2-input: found a handler for part 'test:ping'
688 bundle2-input: found a handler for part 'test:ping'
685 bundle2-input-part: "test:ping" (advisory) supported
689 bundle2-input-part: "test:ping" (advisory) supported
686 received ping request (id 6)
690 received ping request (id 6)
687 bundle2-input: payload chunk size: 0
691 bundle2-input: payload chunk size: 0
688 bundle2-input: part header size: 0
692 bundle2-input: part header size: 0
689 bundle2-input: end of bundle2 stream
693 bundle2-input: end of bundle2 stream
690 bundle2-input-bundle: 6 parts total
694 bundle2-input-bundle: 6 parts total
691 0 unread bytes
695 0 unread bytes
692 3 total verses sung
696 3 total verses sung
693
697
694 Unbundle with an unknown mandatory part
698 Unbundle with an unknown mandatory part
695 (should abort)
699 (should abort)
696
700
697 $ hg bundle2 --parts --unknown ../unknown.hg2
701 $ hg bundle2 --parts --unknown ../unknown.hg2
698
702
699 $ hg unbundle2 < ../unknown.hg2
703 $ hg unbundle2 < ../unknown.hg2
700 The choir starts singing:
704 The choir starts singing:
701 Patali Dirapata, Cromda Cromda Ripalo, Pata Pata, Ko Ko Ko
705 Patali Dirapata, Cromda Cromda Ripalo, Pata Pata, Ko Ko Ko
702 Bokoro Dipoulito, Rondi Rondi Pepino, Pata Pata, Ko Ko Ko
706 Bokoro Dipoulito, Rondi Rondi Pepino, Pata Pata, Ko Ko Ko
703 Emana Karassoli, Loucra Loucra Ponponto, Pata Pata, Ko Ko Ko.
707 Emana Karassoli, Loucra Loucra Ponponto, Pata Pata, Ko Ko Ko.
704 debugreply: no reply
708 debugreply: no reply
705 0 unread bytes
709 0 unread bytes
706 abort: missing support for test:unknown
710 abort: missing support for test:unknown
707 [255]
711 [255]
708
712
709 Unbundle with an unknown mandatory part parameters
713 Unbundle with an unknown mandatory part parameters
710 (should abort)
714 (should abort)
711
715
712 $ hg bundle2 --unknownparams ../unknown.hg2
716 $ hg bundle2 --unknownparams ../unknown.hg2
713
717
714 $ hg unbundle2 < ../unknown.hg2
718 $ hg unbundle2 < ../unknown.hg2
715 0 unread bytes
719 0 unread bytes
716 abort: missing support for test:song - randomparams
720 abort: missing support for test:song - randomparams
717 [255]
721 [255]
718
722
719 unbundle with a reply
723 unbundle with a reply
720
724
721 $ hg bundle2 --parts --reply ../parts-reply.hg2
725 $ hg bundle2 --parts --reply ../parts-reply.hg2
722 $ hg unbundle2 ../reply.hg2 < ../parts-reply.hg2
726 $ hg unbundle2 ../reply.hg2 < ../parts-reply.hg2
723 0 unread bytes
727 0 unread bytes
724 3 total verses sung
728 3 total verses sung
725
729
726 The reply is a bundle
730 The reply is a bundle
727
731
728 $ f --hexdump ../reply.hg2
732 $ f --hexdump ../reply.hg2
729 ../reply.hg2:
733 ../reply.hg2:
730 0000: 48 47 32 30 00 00 00 00 00 00 00 1b 06 6f 75 74 |HG20.........out|
734 0000: 48 47 32 30 00 00 00 00 00 00 00 1b 06 6f 75 74 |HG20.........out|
731 0010: 70 75 74 00 00 00 00 00 01 0b 01 69 6e 2d 72 65 |put........in-re|
735 0010: 70 75 74 00 00 00 00 00 01 0b 01 69 6e 2d 72 65 |put........in-re|
732 0020: 70 6c 79 2d 74 6f 33 00 00 00 d9 54 68 65 20 63 |ply-to3....The c|
736 0020: 70 6c 79 2d 74 6f 33 00 00 00 d9 54 68 65 20 63 |ply-to3....The c|
733 0030: 68 6f 69 72 20 73 74 61 72 74 73 20 73 69 6e 67 |hoir starts sing|
737 0030: 68 6f 69 72 20 73 74 61 72 74 73 20 73 69 6e 67 |hoir starts sing|
734 0040: 69 6e 67 3a 0a 20 20 20 20 50 61 74 61 6c 69 20 |ing:. Patali |
738 0040: 69 6e 67 3a 0a 20 20 20 20 50 61 74 61 6c 69 20 |ing:. Patali |
735 0050: 44 69 72 61 70 61 74 61 2c 20 43 72 6f 6d 64 61 |Dirapata, Cromda|
739 0050: 44 69 72 61 70 61 74 61 2c 20 43 72 6f 6d 64 61 |Dirapata, Cromda|
736 0060: 20 43 72 6f 6d 64 61 20 52 69 70 61 6c 6f 2c 20 | Cromda Ripalo, |
740 0060: 20 43 72 6f 6d 64 61 20 52 69 70 61 6c 6f 2c 20 | Cromda Ripalo, |
737 0070: 50 61 74 61 20 50 61 74 61 2c 20 4b 6f 20 4b 6f |Pata Pata, Ko Ko|
741 0070: 50 61 74 61 20 50 61 74 61 2c 20 4b 6f 20 4b 6f |Pata Pata, Ko Ko|
738 0080: 20 4b 6f 0a 20 20 20 20 42 6f 6b 6f 72 6f 20 44 | Ko. Bokoro D|
742 0080: 20 4b 6f 0a 20 20 20 20 42 6f 6b 6f 72 6f 20 44 | Ko. Bokoro D|
739 0090: 69 70 6f 75 6c 69 74 6f 2c 20 52 6f 6e 64 69 20 |ipoulito, Rondi |
743 0090: 69 70 6f 75 6c 69 74 6f 2c 20 52 6f 6e 64 69 20 |ipoulito, Rondi |
740 00a0: 52 6f 6e 64 69 20 50 65 70 69 6e 6f 2c 20 50 61 |Rondi Pepino, Pa|
744 00a0: 52 6f 6e 64 69 20 50 65 70 69 6e 6f 2c 20 50 61 |Rondi Pepino, Pa|
741 00b0: 74 61 20 50 61 74 61 2c 20 4b 6f 20 4b 6f 20 4b |ta Pata, Ko Ko K|
745 00b0: 74 61 20 50 61 74 61 2c 20 4b 6f 20 4b 6f 20 4b |ta Pata, Ko Ko K|
742 00c0: 6f 0a 20 20 20 20 45 6d 61 6e 61 20 4b 61 72 61 |o. Emana Kara|
746 00c0: 6f 0a 20 20 20 20 45 6d 61 6e 61 20 4b 61 72 61 |o. Emana Kara|
743 00d0: 73 73 6f 6c 69 2c 20 4c 6f 75 63 72 61 20 4c 6f |ssoli, Loucra Lo|
747 00d0: 73 73 6f 6c 69 2c 20 4c 6f 75 63 72 61 20 4c 6f |ssoli, Loucra Lo|
744 00e0: 75 63 72 61 20 50 6f 6e 70 6f 6e 74 6f 2c 20 50 |ucra Ponponto, P|
748 00e0: 75 63 72 61 20 50 6f 6e 70 6f 6e 74 6f 2c 20 50 |ucra Ponponto, P|
745 00f0: 61 74 61 20 50 61 74 61 2c 20 4b 6f 20 4b 6f 20 |ata Pata, Ko Ko |
749 00f0: 61 74 61 20 50 61 74 61 2c 20 4b 6f 20 4b 6f 20 |ata Pata, Ko Ko |
746 0100: 4b 6f 2e 0a 00 00 00 00 00 00 00 1b 06 6f 75 74 |Ko...........out|
750 0100: 4b 6f 2e 0a 00 00 00 00 00 00 00 1b 06 6f 75 74 |Ko...........out|
747 0110: 70 75 74 00 00 00 01 00 01 0b 01 69 6e 2d 72 65 |put........in-re|
751 0110: 70 75 74 00 00 00 01 00 01 0b 01 69 6e 2d 72 65 |put........in-re|
748 0120: 70 6c 79 2d 74 6f 34 00 00 00 c9 64 65 62 75 67 |ply-to4....debug|
752 0120: 70 6c 79 2d 74 6f 34 00 00 00 c9 64 65 62 75 67 |ply-to4....debug|
749 0130: 72 65 70 6c 79 3a 20 63 61 70 61 62 69 6c 69 74 |reply: capabilit|
753 0130: 72 65 70 6c 79 3a 20 63 61 70 61 62 69 6c 69 74 |reply: capabilit|
750 0140: 69 65 73 3a 0a 64 65 62 75 67 72 65 70 6c 79 3a |ies:.debugreply:|
754 0140: 69 65 73 3a 0a 64 65 62 75 67 72 65 70 6c 79 3a |ies:.debugreply:|
751 0150: 20 20 20 20 20 27 63 69 74 79 3d 21 27 0a 64 65 | 'city=!'.de|
755 0150: 20 20 20 20 20 27 63 69 74 79 3d 21 27 0a 64 65 | 'city=!'.de|
752 0160: 62 75 67 72 65 70 6c 79 3a 20 20 20 20 20 20 20 |bugreply: |
756 0160: 62 75 67 72 65 70 6c 79 3a 20 20 20 20 20 20 20 |bugreply: |
753 0170: 20 20 27 63 65 6c 65 73 74 65 2c 76 69 6c 6c 65 | 'celeste,ville|
757 0170: 20 20 27 63 65 6c 65 73 74 65 2c 76 69 6c 6c 65 | 'celeste,ville|
754 0180: 27 0a 64 65 62 75 67 72 65 70 6c 79 3a 20 20 20 |'.debugreply: |
758 0180: 27 0a 64 65 62 75 67 72 65 70 6c 79 3a 20 20 20 |'.debugreply: |
755 0190: 20 20 27 65 6c 65 70 68 61 6e 74 73 27 0a 64 65 | 'elephants'.de|
759 0190: 20 20 27 65 6c 65 70 68 61 6e 74 73 27 0a 64 65 | 'elephants'.de|
756 01a0: 62 75 67 72 65 70 6c 79 3a 20 20 20 20 20 20 20 |bugreply: |
760 01a0: 62 75 67 72 65 70 6c 79 3a 20 20 20 20 20 20 20 |bugreply: |
757 01b0: 20 20 27 62 61 62 61 72 27 0a 64 65 62 75 67 72 | 'babar'.debugr|
761 01b0: 20 20 27 62 61 62 61 72 27 0a 64 65 62 75 67 72 | 'babar'.debugr|
758 01c0: 65 70 6c 79 3a 20 20 20 20 20 20 20 20 20 27 63 |eply: 'c|
762 01c0: 65 70 6c 79 3a 20 20 20 20 20 20 20 20 20 27 63 |eply: 'c|
759 01d0: 65 6c 65 73 74 65 27 0a 64 65 62 75 67 72 65 70 |eleste'.debugrep|
763 01d0: 65 6c 65 73 74 65 27 0a 64 65 62 75 67 72 65 70 |eleste'.debugrep|
760 01e0: 6c 79 3a 20 20 20 20 20 27 70 69 6e 67 2d 70 6f |ly: 'ping-po|
764 01e0: 6c 79 3a 20 20 20 20 20 27 70 69 6e 67 2d 70 6f |ly: 'ping-po|
761 01f0: 6e 67 27 0a 00 00 00 00 00 00 00 1e 09 74 65 73 |ng'..........tes|
765 01f0: 6e 67 27 0a 00 00 00 00 00 00 00 1e 09 74 65 73 |ng'..........tes|
762 0200: 74 3a 70 6f 6e 67 00 00 00 02 01 00 0b 01 69 6e |t:pong........in|
766 0200: 74 3a 70 6f 6e 67 00 00 00 02 01 00 0b 01 69 6e |t:pong........in|
763 0210: 2d 72 65 70 6c 79 2d 74 6f 37 00 00 00 00 00 00 |-reply-to7......|
767 0210: 2d 72 65 70 6c 79 2d 74 6f 37 00 00 00 00 00 00 |-reply-to7......|
764 0220: 00 1b 06 6f 75 74 70 75 74 00 00 00 03 00 01 0b |...output.......|
768 0220: 00 1b 06 6f 75 74 70 75 74 00 00 00 03 00 01 0b |...output.......|
765 0230: 01 69 6e 2d 72 65 70 6c 79 2d 74 6f 37 00 00 00 |.in-reply-to7...|
769 0230: 01 69 6e 2d 72 65 70 6c 79 2d 74 6f 37 00 00 00 |.in-reply-to7...|
766 0240: 3d 72 65 63 65 69 76 65 64 20 70 69 6e 67 20 72 |=received ping r|
770 0240: 3d 72 65 63 65 69 76 65 64 20 70 69 6e 67 20 72 |=received ping r|
767 0250: 65 71 75 65 73 74 20 28 69 64 20 37 29 0a 72 65 |equest (id 7).re|
771 0250: 65 71 75 65 73 74 20 28 69 64 20 37 29 0a 72 65 |equest (id 7).re|
768 0260: 70 6c 79 69 6e 67 20 74 6f 20 70 69 6e 67 20 72 |plying to ping r|
772 0260: 70 6c 79 69 6e 67 20 74 6f 20 70 69 6e 67 20 72 |plying to ping r|
769 0270: 65 71 75 65 73 74 20 28 69 64 20 37 29 0a 00 00 |equest (id 7)...|
773 0270: 65 71 75 65 73 74 20 28 69 64 20 37 29 0a 00 00 |equest (id 7)...|
770 0280: 00 00 00 00 00 00 |......|
774 0280: 00 00 00 00 00 00 |......|
771
775
772 The reply is valid
776 The reply is valid
773
777
774 $ hg statbundle2 < ../reply.hg2
778 $ hg statbundle2 < ../reply.hg2
775 options count: 0
779 options count: 0
776 :output:
780 :output:
777 mandatory: 0
781 mandatory: 0
778 advisory: 1
782 advisory: 1
779 payload: 217 bytes
783 payload: 217 bytes
780 :output:
784 :output:
781 mandatory: 0
785 mandatory: 0
782 advisory: 1
786 advisory: 1
783 payload: 201 bytes
787 payload: 201 bytes
784 :test:pong:
788 :test:pong:
785 mandatory: 1
789 mandatory: 1
786 advisory: 0
790 advisory: 0
787 payload: 0 bytes
791 payload: 0 bytes
788 :output:
792 :output:
789 mandatory: 0
793 mandatory: 0
790 advisory: 1
794 advisory: 1
791 payload: 61 bytes
795 payload: 61 bytes
792 parts count: 4
796 parts count: 4
793
797
794 Unbundle the reply to get the output:
798 Unbundle the reply to get the output:
795
799
796 $ hg unbundle2 < ../reply.hg2
800 $ hg unbundle2 < ../reply.hg2
797 remote: The choir starts singing:
801 remote: The choir starts singing:
798 remote: Patali Dirapata, Cromda Cromda Ripalo, Pata Pata, Ko Ko Ko
802 remote: Patali Dirapata, Cromda Cromda Ripalo, Pata Pata, Ko Ko Ko
799 remote: Bokoro Dipoulito, Rondi Rondi Pepino, Pata Pata, Ko Ko Ko
803 remote: Bokoro Dipoulito, Rondi Rondi Pepino, Pata Pata, Ko Ko Ko
800 remote: Emana Karassoli, Loucra Loucra Ponponto, Pata Pata, Ko Ko Ko.
804 remote: Emana Karassoli, Loucra Loucra Ponponto, Pata Pata, Ko Ko Ko.
801 remote: debugreply: capabilities:
805 remote: debugreply: capabilities:
802 remote: debugreply: 'city=!'
806 remote: debugreply: 'city=!'
803 remote: debugreply: 'celeste,ville'
807 remote: debugreply: 'celeste,ville'
804 remote: debugreply: 'elephants'
808 remote: debugreply: 'elephants'
805 remote: debugreply: 'babar'
809 remote: debugreply: 'babar'
806 remote: debugreply: 'celeste'
810 remote: debugreply: 'celeste'
807 remote: debugreply: 'ping-pong'
811 remote: debugreply: 'ping-pong'
808 remote: received ping request (id 7)
812 remote: received ping request (id 7)
809 remote: replying to ping request (id 7)
813 remote: replying to ping request (id 7)
810 0 unread bytes
814 0 unread bytes
811
815
812 Test push race detection
816 Test push race detection
813
817
814 $ hg bundle2 --pushrace ../part-race.hg2
818 $ hg bundle2 --pushrace ../part-race.hg2
815
819
816 $ hg unbundle2 < ../part-race.hg2
820 $ hg unbundle2 < ../part-race.hg2
817 0 unread bytes
821 0 unread bytes
818 abort: push race: repository changed while pushing - please try again
822 abort: push race: repository changed while pushing - please try again
819 [255]
823 [255]
820
824
821 Support for changegroup
825 Support for changegroup
822 ===================================
826 ===================================
823
827
824 $ hg unbundle $TESTDIR/bundles/rebase.hg
828 $ hg unbundle $TESTDIR/bundles/rebase.hg
825 adding changesets
829 adding changesets
826 adding manifests
830 adding manifests
827 adding file changes
831 adding file changes
828 added 8 changesets with 7 changes to 7 files (+3 heads)
832 added 8 changesets with 7 changes to 7 files (+3 heads)
829 (run 'hg heads' to see heads, 'hg merge' to merge)
833 (run 'hg heads' to see heads, 'hg merge' to merge)
830
834
831 $ hg log -G
835 $ hg log -G
832 o 8:02de42196ebe draft Nicolas Dumazet <nicdumz.commits@gmail.com> H
836 o 8:02de42196ebe draft Nicolas Dumazet <nicdumz.commits@gmail.com> H
833 |
837 |
834 | o 7:eea13746799a draft Nicolas Dumazet <nicdumz.commits@gmail.com> G
838 | o 7:eea13746799a draft Nicolas Dumazet <nicdumz.commits@gmail.com> G
835 |/|
839 |/|
836 o | 6:24b6387c8c8c draft Nicolas Dumazet <nicdumz.commits@gmail.com> F
840 o | 6:24b6387c8c8c draft Nicolas Dumazet <nicdumz.commits@gmail.com> F
837 | |
841 | |
838 | o 5:9520eea781bc draft Nicolas Dumazet <nicdumz.commits@gmail.com> E
842 | o 5:9520eea781bc draft Nicolas Dumazet <nicdumz.commits@gmail.com> E
839 |/
843 |/
840 | o 4:32af7686d403 draft Nicolas Dumazet <nicdumz.commits@gmail.com> D
844 | o 4:32af7686d403 draft Nicolas Dumazet <nicdumz.commits@gmail.com> D
841 | |
845 | |
842 | o 3:5fddd98957c8 draft Nicolas Dumazet <nicdumz.commits@gmail.com> C
846 | o 3:5fddd98957c8 draft Nicolas Dumazet <nicdumz.commits@gmail.com> C
843 | |
847 | |
844 | o 2:42ccdea3bb16 draft Nicolas Dumazet <nicdumz.commits@gmail.com> B
848 | o 2:42ccdea3bb16 draft Nicolas Dumazet <nicdumz.commits@gmail.com> B
845 |/
849 |/
846 o 1:cd010b8cd998 draft Nicolas Dumazet <nicdumz.commits@gmail.com> A
850 o 1:cd010b8cd998 draft Nicolas Dumazet <nicdumz.commits@gmail.com> A
847
851
848 @ 0:3903775176ed draft test a
852 @ 0:3903775176ed draft test a
849
853
850
854
851 $ hg bundle2 --debug --config progress.debug=true --config devel.bundle2.debug=true --rev '8+7+5+4' ../rev.hg2
855 $ hg bundle2 --debug --config progress.debug=true --config devel.bundle2.debug=true --rev '8+7+5+4' ../rev.hg2
852 4 changesets found
856 4 changesets found
853 list of changesets:
857 list of changesets:
854 32af7686d403cf45b5d95f2d70cebea587ac806a
858 32af7686d403cf45b5d95f2d70cebea587ac806a
855 9520eea781bcca16c1e15acc0ba14335a0e8e5ba
859 9520eea781bcca16c1e15acc0ba14335a0e8e5ba
856 eea13746799a9e0bfd88f29d3c2e9dc9389f524f
860 eea13746799a9e0bfd88f29d3c2e9dc9389f524f
857 02de42196ebee42ef284b6780a87cdc96e8eaab6
861 02de42196ebee42ef284b6780a87cdc96e8eaab6
858 bundle2-output-bundle: "HG20", 1 parts total
862 bundle2-output-bundle: "HG20", 1 parts total
859 bundle2-output: start emission of HG20 stream
863 bundle2-output: start emission of HG20 stream
860 bundle2-output: bundle parameter:
864 bundle2-output: bundle parameter:
861 bundle2-output: start of parts
865 bundle2-output: start of parts
862 bundle2-output: bundle part: "changegroup"
866 bundle2-output: bundle part: "changegroup"
863 bundle2-output-part: "changegroup" (advisory) streamed payload
867 bundle2-output-part: "changegroup" (advisory) streamed payload
864 bundle2-output: part 0: "changegroup"
868 bundle2-output: part 0: "changegroup"
865 bundle2-output: header chunk size: 18
869 bundle2-output: header chunk size: 18
866 bundling: 1/4 changesets (25.00%)
870 bundling: 1/4 changesets (25.00%)
867 bundling: 2/4 changesets (50.00%)
871 bundling: 2/4 changesets (50.00%)
868 bundling: 3/4 changesets (75.00%)
872 bundling: 3/4 changesets (75.00%)
869 bundling: 4/4 changesets (100.00%)
873 bundling: 4/4 changesets (100.00%)
870 bundling: 1/4 manifests (25.00%)
874 bundling: 1/4 manifests (25.00%)
871 bundling: 2/4 manifests (50.00%)
875 bundling: 2/4 manifests (50.00%)
872 bundling: 3/4 manifests (75.00%)
876 bundling: 3/4 manifests (75.00%)
873 bundling: 4/4 manifests (100.00%)
877 bundling: 4/4 manifests (100.00%)
874 bundling: D 1/3 files (33.33%)
878 bundling: D 1/3 files (33.33%)
875 bundling: E 2/3 files (66.67%)
879 bundling: E 2/3 files (66.67%)
876 bundling: H 3/3 files (100.00%)
880 bundling: H 3/3 files (100.00%)
877 bundle2-output: payload chunk size: 1555
881 bundle2-output: payload chunk size: 1555
878 bundle2-output: closing payload chunk
882 bundle2-output: closing payload chunk
879 bundle2-output: end of bundle
883 bundle2-output: end of bundle
880
884
881 $ f --hexdump ../rev.hg2
885 $ f --hexdump ../rev.hg2
882 ../rev.hg2:
886 ../rev.hg2:
883 0000: 48 47 32 30 00 00 00 00 00 00 00 12 0b 63 68 61 |HG20.........cha|
887 0000: 48 47 32 30 00 00 00 00 00 00 00 12 0b 63 68 61 |HG20.........cha|
884 0010: 6e 67 65 67 72 6f 75 70 00 00 00 00 00 00 00 00 |ngegroup........|
888 0010: 6e 67 65 67 72 6f 75 70 00 00 00 00 00 00 00 00 |ngegroup........|
885 0020: 06 13 00 00 00 a4 32 af 76 86 d4 03 cf 45 b5 d9 |......2.v....E..|
889 0020: 06 13 00 00 00 a4 32 af 76 86 d4 03 cf 45 b5 d9 |......2.v....E..|
886 0030: 5f 2d 70 ce be a5 87 ac 80 6a 5f dd d9 89 57 c8 |_-p......j_...W.|
890 0030: 5f 2d 70 ce be a5 87 ac 80 6a 5f dd d9 89 57 c8 |_-p......j_...W.|
887 0040: a5 4a 4d 43 6d fe 1d a9 d8 7f 21 a1 b9 7b 00 00 |.JMCm.....!..{..|
891 0040: a5 4a 4d 43 6d fe 1d a9 d8 7f 21 a1 b9 7b 00 00 |.JMCm.....!..{..|
888 0050: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
892 0050: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
889 0060: 00 00 32 af 76 86 d4 03 cf 45 b5 d9 5f 2d 70 ce |..2.v....E.._-p.|
893 0060: 00 00 32 af 76 86 d4 03 cf 45 b5 d9 5f 2d 70 ce |..2.v....E.._-p.|
890 0070: be a5 87 ac 80 6a 00 00 00 00 00 00 00 29 00 00 |.....j.......)..|
894 0070: be a5 87 ac 80 6a 00 00 00 00 00 00 00 29 00 00 |.....j.......)..|
891 0080: 00 29 36 65 31 66 34 63 34 37 65 63 62 35 33 33 |.)6e1f4c47ecb533|
895 0080: 00 29 36 65 31 66 34 63 34 37 65 63 62 35 33 33 |.)6e1f4c47ecb533|
892 0090: 66 66 64 30 63 38 65 35 32 63 64 63 38 38 61 66 |ffd0c8e52cdc88af|
896 0090: 66 66 64 30 63 38 65 35 32 63 64 63 38 38 61 66 |ffd0c8e52cdc88af|
893 00a0: 62 36 63 64 33 39 65 32 30 63 0a 00 00 00 66 00 |b6cd39e20c....f.|
897 00a0: 62 36 63 64 33 39 65 32 30 63 0a 00 00 00 66 00 |b6cd39e20c....f.|
894 00b0: 00 00 68 00 00 00 02 44 0a 00 00 00 69 00 00 00 |..h....D....i...|
898 00b0: 00 00 68 00 00 00 02 44 0a 00 00 00 69 00 00 00 |..h....D....i...|
895 00c0: 6a 00 00 00 01 44 00 00 00 a4 95 20 ee a7 81 bc |j....D..... ....|
899 00c0: 6a 00 00 00 01 44 00 00 00 a4 95 20 ee a7 81 bc |j....D..... ....|
896 00d0: ca 16 c1 e1 5a cc 0b a1 43 35 a0 e8 e5 ba cd 01 |....Z...C5......|
900 00d0: ca 16 c1 e1 5a cc 0b a1 43 35 a0 e8 e5 ba cd 01 |....Z...C5......|
897 00e0: 0b 8c d9 98 f3 98 1a 5a 81 15 f9 4f 8d a4 ab 50 |.......Z...O...P|
901 00e0: 0b 8c d9 98 f3 98 1a 5a 81 15 f9 4f 8d a4 ab 50 |.......Z...O...P|
898 00f0: 60 89 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |`...............|
902 00f0: 60 89 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |`...............|
899 0100: 00 00 00 00 00 00 95 20 ee a7 81 bc ca 16 c1 e1 |....... ........|
903 0100: 00 00 00 00 00 00 95 20 ee a7 81 bc ca 16 c1 e1 |....... ........|
900 0110: 5a cc 0b a1 43 35 a0 e8 e5 ba 00 00 00 00 00 00 |Z...C5..........|
904 0110: 5a cc 0b a1 43 35 a0 e8 e5 ba 00 00 00 00 00 00 |Z...C5..........|
901 0120: 00 29 00 00 00 29 34 64 65 63 65 39 63 38 32 36 |.)...)4dece9c826|
905 0120: 00 29 00 00 00 29 34 64 65 63 65 39 63 38 32 36 |.)...)4dece9c826|
902 0130: 66 36 39 34 39 30 35 30 37 62 39 38 63 36 33 38 |f69490507b98c638|
906 0130: 66 36 39 34 39 30 35 30 37 62 39 38 63 36 33 38 |f69490507b98c638|
903 0140: 33 61 33 30 30 39 62 32 39 35 38 33 37 64 0a 00 |3a3009b295837d..|
907 0140: 33 61 33 30 30 39 62 32 39 35 38 33 37 64 0a 00 |3a3009b295837d..|
904 0150: 00 00 66 00 00 00 68 00 00 00 02 45 0a 00 00 00 |..f...h....E....|
908 0150: 00 00 66 00 00 00 68 00 00 00 02 45 0a 00 00 00 |..f...h....E....|
905 0160: 69 00 00 00 6a 00 00 00 01 45 00 00 00 a2 ee a1 |i...j....E......|
909 0160: 69 00 00 00 6a 00 00 00 01 45 00 00 00 a2 ee a1 |i...j....E......|
906 0170: 37 46 79 9a 9e 0b fd 88 f2 9d 3c 2e 9d c9 38 9f |7Fy.......<...8.|
910 0170: 37 46 79 9a 9e 0b fd 88 f2 9d 3c 2e 9d c9 38 9f |7Fy.......<...8.|
907 0180: 52 4f 24 b6 38 7c 8c 8c ae 37 17 88 80 f3 fa 95 |RO$.8|...7......|
911 0180: 52 4f 24 b6 38 7c 8c 8c ae 37 17 88 80 f3 fa 95 |RO$.8|...7......|
908 0190: de d3 cb 1c f7 85 95 20 ee a7 81 bc ca 16 c1 e1 |....... ........|
912 0190: de d3 cb 1c f7 85 95 20 ee a7 81 bc ca 16 c1 e1 |....... ........|
909 01a0: 5a cc 0b a1 43 35 a0 e8 e5 ba ee a1 37 46 79 9a |Z...C5......7Fy.|
913 01a0: 5a cc 0b a1 43 35 a0 e8 e5 ba ee a1 37 46 79 9a |Z...C5......7Fy.|
910 01b0: 9e 0b fd 88 f2 9d 3c 2e 9d c9 38 9f 52 4f 00 00 |......<...8.RO..|
914 01b0: 9e 0b fd 88 f2 9d 3c 2e 9d c9 38 9f 52 4f 00 00 |......<...8.RO..|
911 01c0: 00 00 00 00 00 29 00 00 00 29 33 36 35 62 39 33 |.....)...)365b93|
915 01c0: 00 00 00 00 00 29 00 00 00 29 33 36 35 62 39 33 |.....)...)365b93|
912 01d0: 64 35 37 66 64 66 34 38 31 34 65 32 62 35 39 31 |d57fdf4814e2b591|
916 01d0: 64 35 37 66 64 66 34 38 31 34 65 32 62 35 39 31 |d57fdf4814e2b591|
913 01e0: 31 64 36 62 61 63 66 66 32 62 31 32 30 31 34 34 |1d6bacff2b120144|
917 01e0: 31 64 36 62 61 63 66 66 32 62 31 32 30 31 34 34 |1d6bacff2b120144|
914 01f0: 34 31 0a 00 00 00 66 00 00 00 68 00 00 00 00 00 |41....f...h.....|
918 01f0: 34 31 0a 00 00 00 66 00 00 00 68 00 00 00 00 00 |41....f...h.....|
915 0200: 00 00 69 00 00 00 6a 00 00 00 01 47 00 00 00 a4 |..i...j....G....|
919 0200: 00 00 69 00 00 00 6a 00 00 00 01 47 00 00 00 a4 |..i...j....G....|
916 0210: 02 de 42 19 6e be e4 2e f2 84 b6 78 0a 87 cd c9 |..B.n......x....|
920 0210: 02 de 42 19 6e be e4 2e f2 84 b6 78 0a 87 cd c9 |..B.n......x....|
917 0220: 6e 8e aa b6 24 b6 38 7c 8c 8c ae 37 17 88 80 f3 |n...$.8|...7....|
921 0220: 6e 8e aa b6 24 b6 38 7c 8c 8c ae 37 17 88 80 f3 |n...$.8|...7....|
918 0230: fa 95 de d3 cb 1c f7 85 00 00 00 00 00 00 00 00 |................|
922 0230: fa 95 de d3 cb 1c f7 85 00 00 00 00 00 00 00 00 |................|
919 0240: 00 00 00 00 00 00 00 00 00 00 00 00 02 de 42 19 |..............B.|
923 0240: 00 00 00 00 00 00 00 00 00 00 00 00 02 de 42 19 |..............B.|
920 0250: 6e be e4 2e f2 84 b6 78 0a 87 cd c9 6e 8e aa b6 |n......x....n...|
924 0250: 6e be e4 2e f2 84 b6 78 0a 87 cd c9 6e 8e aa b6 |n......x....n...|
921 0260: 00 00 00 00 00 00 00 29 00 00 00 29 38 62 65 65 |.......)...)8bee|
925 0260: 00 00 00 00 00 00 00 29 00 00 00 29 38 62 65 65 |.......)...)8bee|
922 0270: 34 38 65 64 63 37 33 31 38 35 34 31 66 63 30 30 |48edc7318541fc00|
926 0270: 34 38 65 64 63 37 33 31 38 35 34 31 66 63 30 30 |48edc7318541fc00|
923 0280: 31 33 65 65 34 31 62 30 38 39 32 37 36 61 38 63 |13ee41b089276a8c|
927 0280: 31 33 65 65 34 31 62 30 38 39 32 37 36 61 38 63 |13ee41b089276a8c|
924 0290: 32 34 62 66 0a 00 00 00 66 00 00 00 66 00 00 00 |24bf....f...f...|
928 0290: 32 34 62 66 0a 00 00 00 66 00 00 00 66 00 00 00 |24bf....f...f...|
925 02a0: 02 48 0a 00 00 00 67 00 00 00 68 00 00 00 01 48 |.H....g...h....H|
929 02a0: 02 48 0a 00 00 00 67 00 00 00 68 00 00 00 01 48 |.H....g...h....H|
926 02b0: 00 00 00 00 00 00 00 8b 6e 1f 4c 47 ec b5 33 ff |........n.LG..3.|
930 02b0: 00 00 00 00 00 00 00 8b 6e 1f 4c 47 ec b5 33 ff |........n.LG..3.|
927 02c0: d0 c8 e5 2c dc 88 af b6 cd 39 e2 0c 66 a5 a0 18 |...,.....9..f...|
931 02c0: d0 c8 e5 2c dc 88 af b6 cd 39 e2 0c 66 a5 a0 18 |...,.....9..f...|
928 02d0: 17 fd f5 23 9c 27 38 02 b5 b7 61 8d 05 1c 89 e4 |...#.'8...a.....|
932 02d0: 17 fd f5 23 9c 27 38 02 b5 b7 61 8d 05 1c 89 e4 |...#.'8...a.....|
929 02e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
933 02e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
930 02f0: 00 00 00 00 32 af 76 86 d4 03 cf 45 b5 d9 5f 2d |....2.v....E.._-|
934 02f0: 00 00 00 00 32 af 76 86 d4 03 cf 45 b5 d9 5f 2d |....2.v....E.._-|
931 0300: 70 ce be a5 87 ac 80 6a 00 00 00 81 00 00 00 81 |p......j........|
935 0300: 70 ce be a5 87 ac 80 6a 00 00 00 81 00 00 00 81 |p......j........|
932 0310: 00 00 00 2b 44 00 63 33 66 31 63 61 32 39 32 34 |...+D.c3f1ca2924|
936 0310: 00 00 00 2b 44 00 63 33 66 31 63 61 32 39 32 34 |...+D.c3f1ca2924|
933 0320: 63 31 36 61 31 39 62 30 36 35 36 61 38 34 39 30 |c16a19b0656a8490|
937 0320: 63 31 36 61 31 39 62 30 36 35 36 61 38 34 39 30 |c16a19b0656a8490|
934 0330: 30 65 35 30 34 65 35 62 30 61 65 63 32 64 0a 00 |0e504e5b0aec2d..|
938 0330: 30 65 35 30 34 65 35 62 30 61 65 63 32 64 0a 00 |0e504e5b0aec2d..|
935 0340: 00 00 8b 4d ec e9 c8 26 f6 94 90 50 7b 98 c6 38 |...M...&...P{..8|
939 0340: 00 00 8b 4d ec e9 c8 26 f6 94 90 50 7b 98 c6 38 |...M...&...P{..8|
936 0350: 3a 30 09 b2 95 83 7d 00 7d 8c 9d 88 84 13 25 f5 |:0....}.}.....%.|
940 0350: 3a 30 09 b2 95 83 7d 00 7d 8c 9d 88 84 13 25 f5 |:0....}.}.....%.|
937 0360: c6 b0 63 71 b3 5b 4e 8a 2b 1a 83 00 00 00 00 00 |..cq.[N.+.......|
941 0360: c6 b0 63 71 b3 5b 4e 8a 2b 1a 83 00 00 00 00 00 |..cq.[N.+.......|
938 0370: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 95 |................|
942 0370: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 95 |................|
939 0380: 20 ee a7 81 bc ca 16 c1 e1 5a cc 0b a1 43 35 a0 | ........Z...C5.|
943 0380: 20 ee a7 81 bc ca 16 c1 e1 5a cc 0b a1 43 35 a0 | ........Z...C5.|
940 0390: e8 e5 ba 00 00 00 2b 00 00 00 ac 00 00 00 2b 45 |......+.......+E|
944 0390: e8 e5 ba 00 00 00 2b 00 00 00 ac 00 00 00 2b 45 |......+.......+E|
941 03a0: 00 39 63 36 66 64 30 33 35 30 61 36 63 30 64 30 |.9c6fd0350a6c0d0|
945 03a0: 00 39 63 36 66 64 30 33 35 30 61 36 63 30 64 30 |.9c6fd0350a6c0d0|
942 03b0: 63 34 39 64 34 61 39 63 35 30 31 37 63 66 30 37 |c49d4a9c5017cf07|
946 03b0: 63 34 39 64 34 61 39 63 35 30 31 37 63 66 30 37 |c49d4a9c5017cf07|
943 03c0: 30 34 33 66 35 34 65 35 38 0a 00 00 00 8b 36 5b |043f54e58.....6[|
947 03c0: 30 34 33 66 35 34 65 35 38 0a 00 00 00 8b 36 5b |043f54e58.....6[|
944 03d0: 93 d5 7f df 48 14 e2 b5 91 1d 6b ac ff 2b 12 01 |....H.....k..+..|
948 03d0: 93 d5 7f df 48 14 e2 b5 91 1d 6b ac ff 2b 12 01 |....H.....k..+..|
945 03e0: 44 41 28 a5 84 c6 5e f1 21 f8 9e b6 6a b7 d0 bc |DA(...^.!...j...|
949 03e0: 44 41 28 a5 84 c6 5e f1 21 f8 9e b6 6a b7 d0 bc |DA(...^.!...j...|
946 03f0: 15 3d 80 99 e7 ce 4d ec e9 c8 26 f6 94 90 50 7b |.=....M...&...P{|
950 03f0: 15 3d 80 99 e7 ce 4d ec e9 c8 26 f6 94 90 50 7b |.=....M...&...P{|
947 0400: 98 c6 38 3a 30 09 b2 95 83 7d ee a1 37 46 79 9a |..8:0....}..7Fy.|
951 0400: 98 c6 38 3a 30 09 b2 95 83 7d ee a1 37 46 79 9a |..8:0....}..7Fy.|
948 0410: 9e 0b fd 88 f2 9d 3c 2e 9d c9 38 9f 52 4f 00 00 |......<...8.RO..|
952 0410: 9e 0b fd 88 f2 9d 3c 2e 9d c9 38 9f 52 4f 00 00 |......<...8.RO..|
949 0420: 00 56 00 00 00 56 00 00 00 2b 46 00 32 32 62 66 |.V...V...+F.22bf|
953 0420: 00 56 00 00 00 56 00 00 00 2b 46 00 32 32 62 66 |.V...V...+F.22bf|
950 0430: 63 66 64 36 32 61 32 31 61 33 32 38 37 65 64 62 |cfd62a21a3287edb|
954 0430: 63 66 64 36 32 61 32 31 61 33 32 38 37 65 64 62 |cfd62a21a3287edb|
951 0440: 64 34 64 36 35 36 32 31 38 64 30 66 35 32 35 65 |d4d656218d0f525e|
955 0440: 64 34 64 36 35 36 32 31 38 64 30 66 35 32 35 65 |d4d656218d0f525e|
952 0450: 64 37 36 61 0a 00 00 00 97 8b ee 48 ed c7 31 85 |d76a.......H..1.|
956 0450: 64 37 36 61 0a 00 00 00 97 8b ee 48 ed c7 31 85 |d76a.......H..1.|
953 0460: 41 fc 00 13 ee 41 b0 89 27 6a 8c 24 bf 28 a5 84 |A....A..'j.$.(..|
957 0460: 41 fc 00 13 ee 41 b0 89 27 6a 8c 24 bf 28 a5 84 |A....A..'j.$.(..|
954 0470: c6 5e f1 21 f8 9e b6 6a b7 d0 bc 15 3d 80 99 e7 |.^.!...j....=...|
958 0470: c6 5e f1 21 f8 9e b6 6a b7 d0 bc 15 3d 80 99 e7 |.^.!...j....=...|
955 0480: ce 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
959 0480: ce 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
956 0490: 00 00 00 00 00 02 de 42 19 6e be e4 2e f2 84 b6 |.......B.n......|
960 0490: 00 00 00 00 00 02 de 42 19 6e be e4 2e f2 84 b6 |.......B.n......|
957 04a0: 78 0a 87 cd c9 6e 8e aa b6 00 00 00 2b 00 00 00 |x....n......+...|
961 04a0: 78 0a 87 cd c9 6e 8e aa b6 00 00 00 2b 00 00 00 |x....n......+...|
958 04b0: 56 00 00 00 00 00 00 00 81 00 00 00 81 00 00 00 |V...............|
962 04b0: 56 00 00 00 00 00 00 00 81 00 00 00 81 00 00 00 |V...............|
959 04c0: 2b 48 00 38 35 30 30 31 38 39 65 37 34 61 39 65 |+H.8500189e74a9e|
963 04c0: 2b 48 00 38 35 30 30 31 38 39 65 37 34 61 39 65 |+H.8500189e74a9e|
960 04d0: 30 34 37 35 65 38 32 32 30 39 33 62 63 37 64 62 |0475e822093bc7db|
964 04d0: 30 34 37 35 65 38 32 32 30 39 33 62 63 37 64 62 |0475e822093bc7db|
961 04e0: 30 64 36 33 31 61 65 62 30 62 34 0a 00 00 00 00 |0d631aeb0b4.....|
965 04e0: 30 64 36 33 31 61 65 62 30 62 34 0a 00 00 00 00 |0d631aeb0b4.....|
962 04f0: 00 00 00 05 44 00 00 00 62 c3 f1 ca 29 24 c1 6a |....D...b...)$.j|
966 04f0: 00 00 00 05 44 00 00 00 62 c3 f1 ca 29 24 c1 6a |....D...b...)$.j|
963 0500: 19 b0 65 6a 84 90 0e 50 4e 5b 0a ec 2d 00 00 00 |..ej...PN[..-...|
967 0500: 19 b0 65 6a 84 90 0e 50 4e 5b 0a ec 2d 00 00 00 |..ej...PN[..-...|
964 0510: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
968 0510: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
965 0520: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
969 0520: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
966 0530: 00 00 00 00 00 32 af 76 86 d4 03 cf 45 b5 d9 5f |.....2.v....E.._|
970 0530: 00 00 00 00 00 32 af 76 86 d4 03 cf 45 b5 d9 5f |.....2.v....E.._|
967 0540: 2d 70 ce be a5 87 ac 80 6a 00 00 00 00 00 00 00 |-p......j.......|
971 0540: 2d 70 ce be a5 87 ac 80 6a 00 00 00 00 00 00 00 |-p......j.......|
968 0550: 00 00 00 00 02 44 0a 00 00 00 00 00 00 00 05 45 |.....D.........E|
972 0550: 00 00 00 00 02 44 0a 00 00 00 00 00 00 00 05 45 |.....D.........E|
969 0560: 00 00 00 62 9c 6f d0 35 0a 6c 0d 0c 49 d4 a9 c5 |...b.o.5.l..I...|
973 0560: 00 00 00 62 9c 6f d0 35 0a 6c 0d 0c 49 d4 a9 c5 |...b.o.5.l..I...|
970 0570: 01 7c f0 70 43 f5 4e 58 00 00 00 00 00 00 00 00 |.|.pC.NX........|
974 0570: 01 7c f0 70 43 f5 4e 58 00 00 00 00 00 00 00 00 |.|.pC.NX........|
971 0580: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
975 0580: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
972 0590: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
976 0590: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
973 05a0: 95 20 ee a7 81 bc ca 16 c1 e1 5a cc 0b a1 43 35 |. ........Z...C5|
977 05a0: 95 20 ee a7 81 bc ca 16 c1 e1 5a cc 0b a1 43 35 |. ........Z...C5|
974 05b0: a0 e8 e5 ba 00 00 00 00 00 00 00 00 00 00 00 02 |................|
978 05b0: a0 e8 e5 ba 00 00 00 00 00 00 00 00 00 00 00 02 |................|
975 05c0: 45 0a 00 00 00 00 00 00 00 05 48 00 00 00 62 85 |E.........H...b.|
979 05c0: 45 0a 00 00 00 00 00 00 00 05 48 00 00 00 62 85 |E.........H...b.|
976 05d0: 00 18 9e 74 a9 e0 47 5e 82 20 93 bc 7d b0 d6 31 |...t..G^. ..}..1|
980 05d0: 00 18 9e 74 a9 e0 47 5e 82 20 93 bc 7d b0 d6 31 |...t..G^. ..}..1|
977 05e0: ae b0 b4 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
981 05e0: ae b0 b4 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
978 05f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
982 05f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
979 0600: 00 00 00 00 00 00 00 00 00 00 00 02 de 42 19 6e |.............B.n|
983 0600: 00 00 00 00 00 00 00 00 00 00 00 02 de 42 19 6e |.............B.n|
980 0610: be e4 2e f2 84 b6 78 0a 87 cd c9 6e 8e aa b6 00 |......x....n....|
984 0610: be e4 2e f2 84 b6 78 0a 87 cd c9 6e 8e aa b6 00 |......x....n....|
981 0620: 00 00 00 00 00 00 00 00 00 00 02 48 0a 00 00 00 |...........H....|
985 0620: 00 00 00 00 00 00 00 00 00 00 02 48 0a 00 00 00 |...........H....|
982 0630: 00 00 00 00 00 00 00 00 00 00 00 00 00 |.............|
986 0630: 00 00 00 00 00 00 00 00 00 00 00 00 00 |.............|
983
987
984 $ hg debugbundle ../rev.hg2
988 $ hg debugbundle ../rev.hg2
985 Stream params: {}
989 Stream params: {}
986 changegroup -- '{}'
990 changegroup -- '{}'
987 32af7686d403cf45b5d95f2d70cebea587ac806a
991 32af7686d403cf45b5d95f2d70cebea587ac806a
988 9520eea781bcca16c1e15acc0ba14335a0e8e5ba
992 9520eea781bcca16c1e15acc0ba14335a0e8e5ba
989 eea13746799a9e0bfd88f29d3c2e9dc9389f524f
993 eea13746799a9e0bfd88f29d3c2e9dc9389f524f
990 02de42196ebee42ef284b6780a87cdc96e8eaab6
994 02de42196ebee42ef284b6780a87cdc96e8eaab6
991 $ hg unbundle ../rev.hg2
995 $ hg unbundle ../rev.hg2
992 adding changesets
996 adding changesets
993 adding manifests
997 adding manifests
994 adding file changes
998 adding file changes
995 added 0 changesets with 0 changes to 3 files
999 added 0 changesets with 0 changes to 3 files
996
1000
997 with reply
1001 with reply
998
1002
999 $ hg bundle2 --rev '8+7+5+4' --reply ../rev-rr.hg2
1003 $ hg bundle2 --rev '8+7+5+4' --reply ../rev-rr.hg2
1000 $ hg unbundle2 ../rev-reply.hg2 < ../rev-rr.hg2
1004 $ hg unbundle2 ../rev-reply.hg2 < ../rev-rr.hg2
1001 0 unread bytes
1005 0 unread bytes
1002 addchangegroup return: 1
1006 addchangegroup return: 1
1003
1007
1004 $ f --hexdump ../rev-reply.hg2
1008 $ f --hexdump ../rev-reply.hg2
1005 ../rev-reply.hg2:
1009 ../rev-reply.hg2:
1006 0000: 48 47 32 30 00 00 00 00 00 00 00 2f 11 72 65 70 |HG20......./.rep|
1010 0000: 48 47 32 30 00 00 00 00 00 00 00 2f 11 72 65 70 |HG20......./.rep|
1007 0010: 6c 79 3a 63 68 61 6e 67 65 67 72 6f 75 70 00 00 |ly:changegroup..|
1011 0010: 6c 79 3a 63 68 61 6e 67 65 67 72 6f 75 70 00 00 |ly:changegroup..|
1008 0020: 00 00 00 02 0b 01 06 01 69 6e 2d 72 65 70 6c 79 |........in-reply|
1012 0020: 00 00 00 02 0b 01 06 01 69 6e 2d 72 65 70 6c 79 |........in-reply|
1009 0030: 2d 74 6f 31 72 65 74 75 72 6e 31 00 00 00 00 00 |-to1return1.....|
1013 0030: 2d 74 6f 31 72 65 74 75 72 6e 31 00 00 00 00 00 |-to1return1.....|
1010 0040: 00 00 1b 06 6f 75 74 70 75 74 00 00 00 01 00 01 |....output......|
1014 0040: 00 00 1b 06 6f 75 74 70 75 74 00 00 00 01 00 01 |....output......|
1011 0050: 0b 01 69 6e 2d 72 65 70 6c 79 2d 74 6f 31 00 00 |..in-reply-to1..|
1015 0050: 0b 01 69 6e 2d 72 65 70 6c 79 2d 74 6f 31 00 00 |..in-reply-to1..|
1012 0060: 00 64 61 64 64 69 6e 67 20 63 68 61 6e 67 65 73 |.dadding changes|
1016 0060: 00 64 61 64 64 69 6e 67 20 63 68 61 6e 67 65 73 |.dadding changes|
1013 0070: 65 74 73 0a 61 64 64 69 6e 67 20 6d 61 6e 69 66 |ets.adding manif|
1017 0070: 65 74 73 0a 61 64 64 69 6e 67 20 6d 61 6e 69 66 |ets.adding manif|
1014 0080: 65 73 74 73 0a 61 64 64 69 6e 67 20 66 69 6c 65 |ests.adding file|
1018 0080: 65 73 74 73 0a 61 64 64 69 6e 67 20 66 69 6c 65 |ests.adding file|
1015 0090: 20 63 68 61 6e 67 65 73 0a 61 64 64 65 64 20 30 | changes.added 0|
1019 0090: 20 63 68 61 6e 67 65 73 0a 61 64 64 65 64 20 30 | changes.added 0|
1016 00a0: 20 63 68 61 6e 67 65 73 65 74 73 20 77 69 74 68 | changesets with|
1020 00a0: 20 63 68 61 6e 67 65 73 65 74 73 20 77 69 74 68 | changesets with|
1017 00b0: 20 30 20 63 68 61 6e 67 65 73 20 74 6f 20 33 20 | 0 changes to 3 |
1021 00b0: 20 30 20 63 68 61 6e 67 65 73 20 74 6f 20 33 20 | 0 changes to 3 |
1018 00c0: 66 69 6c 65 73 0a 00 00 00 00 00 00 00 00 |files.........|
1022 00c0: 66 69 6c 65 73 0a 00 00 00 00 00 00 00 00 |files.........|
1019
1023
1020 Check handling of exception during generation.
1024 Check handling of exception during generation.
1021 ----------------------------------------------
1025 ----------------------------------------------
1022
1026
1023 $ hg bundle2 --genraise > ../genfailed.hg2
1027 $ hg bundle2 --genraise > ../genfailed.hg2
1024 abort: Someone set up us the bomb!
1028 abort: Someone set up us the bomb!
1025 [255]
1029 [255]
1026
1030
1027 Should still be a valid bundle
1031 Should still be a valid bundle
1028
1032
1029 $ f --hexdump ../genfailed.hg2
1033 $ f --hexdump ../genfailed.hg2
1030 ../genfailed.hg2:
1034 ../genfailed.hg2:
1031 0000: 48 47 32 30 00 00 00 00 00 00 00 0d 06 6f 75 74 |HG20.........out|
1035 0000: 48 47 32 30 00 00 00 00 00 00 00 0d 06 6f 75 74 |HG20.........out|
1032 0010: 70 75 74 00 00 00 00 00 00 ff ff ff ff 00 00 00 |put.............|
1036 0010: 70 75 74 00 00 00 00 00 00 ff ff ff ff 00 00 00 |put.............|
1033 0020: 48 0b 65 72 72 6f 72 3a 61 62 6f 72 74 00 00 00 |H.error:abort...|
1037 0020: 48 0b 65 72 72 6f 72 3a 61 62 6f 72 74 00 00 00 |H.error:abort...|
1034 0030: 00 01 00 07 2d 6d 65 73 73 61 67 65 75 6e 65 78 |....-messageunex|
1038 0030: 00 01 00 07 2d 6d 65 73 73 61 67 65 75 6e 65 78 |....-messageunex|
1035 0040: 70 65 63 74 65 64 20 65 72 72 6f 72 3a 20 53 6f |pected error: So|
1039 0040: 70 65 63 74 65 64 20 65 72 72 6f 72 3a 20 53 6f |pected error: So|
1036 0050: 6d 65 6f 6e 65 20 73 65 74 20 75 70 20 75 73 20 |meone set up us |
1040 0050: 6d 65 6f 6e 65 20 73 65 74 20 75 70 20 75 73 20 |meone set up us |
1037 0060: 74 68 65 20 62 6f 6d 62 21 00 00 00 00 00 00 00 |the bomb!.......|
1041 0060: 74 68 65 20 62 6f 6d 62 21 00 00 00 00 00 00 00 |the bomb!.......|
1038 0070: 00 |.|
1042 0070: 00 |.|
1039
1043
1040 And its handling on the other size raise a clean exception
1044 And its handling on the other size raise a clean exception
1041
1045
1042 $ cat ../genfailed.hg2 | hg unbundle2
1046 $ cat ../genfailed.hg2 | hg unbundle2
1043 0 unread bytes
1047 0 unread bytes
1044 abort: unexpected error: Someone set up us the bomb!
1048 abort: unexpected error: Someone set up us the bomb!
1045 [255]
1049 [255]
1046
1050
1051 Test compression
1052 ================
1053
1054 Simple case where it just work: GZ
1055 ----------------------------------
1056
1057 $ hg bundle2 --compress GZ --rev '8+7+5+4' ../rev.hg2.bz
1058 $ f --hexdump ../rev.hg2.bz
1059
1060 0000: 48 47 32 30 00 00 00 0e 43 6f 6d 70 72 65 73 73 |HG20....Compress|
1061 0010: 69 6f 6e 3d 47 5a 78 9c 95 94 7d 68 95 55 1c c7 |ion=GZx...}h.U..|
1062 0020: 9f 3b 31 e8 ce fa c3 65 be a0 a4 b4 52 b9 29 e7 |.;1....e....R.).|
1063 0030: f5 79 ce 89 fa 63 ed 5e 77 8b 9c c3 3f 2a 1c 68 |.y...c.^w...?*.h|
1064 0040: cf 79 9b dd 6a ae b0 28 74 b8 e5 96 5b bb 86 61 |.y..j..(t...[..a|
1065 0050: a3 15 6e 3a 71 c8 6a e8 a5 da 95 64 28 22 ce 69 |..n:q.j....d(".i|
1066 0060: cd 06 59 34 28 2b 51 2a 58 c3 17 56 2a 9a 9d 67 |..Y4(+Q*X..V*..g|
1067 0070: dc c6 35 9e c4 1d f8 9e 87 f3 9c f3 3b bf 0f bf |..5.........;...|
1068 0080: 97 e3 38 ce f4 42 b9 d6 af ae d2 55 af ae 7b ad |..8..B.....U..{.|
1069 0090: c6 c9 8d bb 8a ec b4 07 ed 7f fd ed d3 53 be 4e |.............S.N|
1070 00a0: f4 0e af 59 52 73 ea 50 d7 96 9e ba d4 9a 1f 87 |...YRs.P........|
1071 00b0: 9b 9f 1d e8 7a 6a 79 e9 cb 7f cf eb fe 7e d3 82 |....zjy......~..|
1072 00c0: ce 2f 36 38 21 23 cc 36 b7 b5 38 90 ab a1 21 92 |./68!#.6..8...!.|
1073 00d0: 78 5a 0a 8a b1 31 0a 48 a6 29 92 4a 32 e6 1b e1 |xZ...1.H.).J2...|
1074 00e0: 4a 85 b9 46 40 46 ed 61 63 b5 d6 aa 20 1e ac 5e |J..F@F.ac... ..^|
1075 00f0: b0 0a ae 8a c4 03 c6 d6 f9 a3 7b eb fb 4e de 7f |..........{..N..|
1076 0100: e4 97 55 5f 15 76 96 d2 5d bf 9d 3f 38 18 29 4c |..U_.v..]..?8.)L|
1077 0110: 0f b7 5d 6e 9b b3 aa 7e c6 d5 15 5b f7 7c 52 f1 |..]n...~...[.|R.|
1078 0120: 7c 73 18 63 98 6d 3e 23 51 5a 6a 2e 19 72 8d cb ||s.c.m>#QZj..r..|
1079 0130: 09 07 14 78 82 33 e9 62 86 7d 0c 00 17 88 53 86 |...x.3.b.}....S.|
1080 0140: 3d 75 0b 63 e2 16 c6 84 9d 76 8f 76 7a cb de fc |=u.c.....v.vz...|
1081 0150: a8 a3 f0 46 d3 a5 f6 c7 96 b6 9f 60 3b 57 ae 28 |...F.......`;W.(|
1082 0160: ce b2 8d e9 f4 3e 6f 66 53 dd e5 6b ad 67 be f9 |.....>ofS..k.g..|
1083 0170: 72 ee 5f 8d 61 3c 61 b6 f9 8c d8 a5 82 63 45 3d |r._.a<a......cE=|
1084 0180: a3 0c 61 90 68 24 28 87 50 b9 c2 97 c6 20 01 11 |..a.h$(.P.... ..|
1085 0190: 80 84 10 98 cf e8 e4 13 96 05 51 2c 38 f3 c4 ec |..........Q,8...|
1086 01a0: ea 43 e7 96 5e 6a c8 be 11 dd 32 78 a2 fa dd 8f |.C..^j....2x....|
1087 01b0: b3 61 84 61 51 0c b3 cd 27 64 42 6b c2 b4 92 1e |.a.aQ...'dBk....|
1088 01c0: 86 8c 12 68 24 00 10 db 7f 50 00 c6 91 e7 fa 4c |...h$....P.....L|
1089 01d0: 22 22 cc bf 84 81 0a 92 c1 aa 2a c7 1b 49 e6 ee |""........*..I..|
1090 01e0: 6b a9 7e e0 e9 b2 91 5e 7c 73 68 e0 fc 23 3f 34 |k.~....^|sh..#?4|
1091 01f0: ed cf 0e f2 b3 d3 4c d7 ae 59 33 6f 8c 3d b8 63 |......L..Y3o.=.c|
1092 0200: 21 2b e8 3d e0 6f 9d 3a b7 f9 dc 24 2a b2 3e a7 |!+.=.o.:...$*.>.|
1093 0210: 58 dc 91 d8 40 e9 23 8e 88 84 ae 0f b9 00 2e b5 |X...@.#.........|
1094 0220: 74 36 f3 40 53 40 34 15 c0 d7 12 8d e7 bb 65 f9 |t6.@S@4.......e.|
1095 0230: c8 ef 03 0f ff f9 fe b6 8a 0d 6d fd ec 51 70 f7 |..........m..Qp.|
1096 0240: a7 ad 9b 6b 9d da 74 7b 53 43 d1 43 63 fd 19 f9 |...k..t{SC.Cc...|
1097 0250: ca 67 95 e5 ef c4 e6 6c 9e 44 e1 c5 ac 7a 82 6f |.g.....l.D...z.o|
1098 0260: c2 e1 d2 b5 2d 81 29 f0 5d 09 6c 6f 10 ae 88 cf |....-.).].lo....|
1099 0270: 25 05 d0 93 06 78 80 60 43 2d 10 1b 47 71 2b b7 |%....x.`C-..Gq+.|
1100 0280: 7f bb e9 a7 e4 7d 67 7b df 9b f7 62 cf cd d8 f4 |.....}g{...b....|
1101 0290: 48 bc 64 51 57 43 ff ea 8b 0b ae 74 64 53 07 86 |H.dQWC.....tdS..|
1102 02a0: fa 66 3c 5e f7 e1 af a7 c2 90 ff a7 be 9e c9 29 |.f<^...........)|
1103 02b0: b6 cc 41 48 18 69 94 8b 7c 04 7d 8c 98 a7 95 50 |..AH.i..|.}....P|
1104 02c0: 44 d9 d0 20 c8 14 30 14 51 ad 6c 16 03 94 0f 5a |D.. ..0.Q.l....Z|
1105 02d0: 46 93 7f 1c 87 8d 25 d7 9d a2 d1 92 4c f3 c2 54 |F.....%.....L..T|
1106 02e0: ba f8 70 18 ca 24 0a 29 96 43 71 f2 93 95 74 18 |..p..$.).Cq...t.|
1107 02f0: b5 65 c4 b8 f6 6c 5c 34 20 1e d5 0c 21 c0 b1 90 |.e...l\4 ...!...|
1108 0300: 9e 12 40 b9 18 fa 5a 00 41 a2 39 d3 a9 c1 73 21 |..@...Z.A.9...s!|
1109 0310: 8e 5e 3c b9 b8 f8 48 6a 76 46 a7 1a b6 dd 5b 51 |.^<...HjvF....[Q|
1110 0320: 5e 19 1d 59 12 c6 32 89 02 9a c0 8f 4f b8 0a ba |^..Y..2.....O...|
1111 0330: 5e ec 58 37 44 a3 2f dd 33 ed c9 d3 dd c7 22 1b |^.X7D./.3.....".|
1112 0340: 2f d4 94 8e 95 3f 77 a7 ae 6e f3 32 8d bb 4a 4c |/....?w..n.2..JL|
1113 0350: b8 0a 5a 43 34 3a b3 3a d6 77 ff 5c b6 fa ad f9 |..ZC4:.:.w.\....|
1114 0360: db fb 6a 33 df c1 7d 99 cf ef d4 d5 6d da 77 7c |..j3..}.....m.w||
1115 0370: 3b 19 fd af c5 3f f1 60 c3 17 |;....?.`..|
1116 $ hg debugbundle ../rev.hg2.bz
1117 Stream params: {'Compression': 'GZ'}
1118 changegroup -- '{}'
1119 32af7686d403cf45b5d95f2d70cebea587ac806a
1120 9520eea781bcca16c1e15acc0ba14335a0e8e5ba
1121 eea13746799a9e0bfd88f29d3c2e9dc9389f524f
1122 02de42196ebee42ef284b6780a87cdc96e8eaab6
1123 $ hg unbundle ../rev.hg2.bz
1124 adding changesets
1125 adding manifests
1126 adding file changes
1127 added 0 changesets with 0 changes to 3 files
1128 Simple case where it just work: BZ
1129 ----------------------------------
1130
1131 $ hg bundle2 --compress BZ --rev '8+7+5+4' ../rev.hg2.bz
1132 $ f --hexdump ../rev.hg2.bz
1133
1134 0000: 48 47 32 30 00 00 00 0e 43 6f 6d 70 72 65 73 73 |HG20....Compress|
1135 0010: 69 6f 6e 3d 42 5a 42 5a 68 39 31 41 59 26 53 59 |ion=BZBZh91AY&SY|
1136 0020: a3 4b 18 3d 00 00 1a 7f ff ff bf 5f f6 ef ef 7f |.K.=......._....|
1137 0030: f6 3f f7 d1 d9 ff ff f7 6e ff ff 6e f7 f6 bd df |.?......n..n....|
1138 0040: b5 ab ff cf 67 f6 e7 7b f7 c0 02 d7 33 82 8b 51 |....g..{....3..Q|
1139 0050: 04 a5 53 d5 3d 27 a0 99 18 4d 0d 34 00 d1 a1 e8 |..S.='...M.4....|
1140 0060: 80 c8 7a 87 a9 a3 43 6a 3d 46 86 26 80 34 3d 40 |..z...Cj=F.&.4=@|
1141 0070: c8 c9 b5 34 f4 8f 48 0f 51 ea 34 34 fd 4d aa 19 |...4..H.Q.44.M..|
1142 0080: 03 40 0c 08 da 86 43 d4 f5 0f 42 1e a0 f3 54 33 |.@....C...B...T3|
1143 0090: 54 d3 13 4d 03 40 32 00 00 32 03 26 80 0d 00 0d |T..M.@2..2.&....|
1144 00a0: 00 68 c8 c8 03 20 32 30 98 8c 80 00 00 03 4d 00 |.h... 20......M.|
1145 00b0: c8 00 00 0d 00 00 22 99 a1 34 c2 64 a6 d5 34 1a |......"..4.d..4.|
1146 00c0: 00 00 06 86 83 4d 07 a8 d1 a0 68 01 a0 00 00 00 |.....M....h.....|
1147 00d0: 00 0d 06 80 00 00 00 0d 00 03 40 00 00 04 a4 a1 |..........@.....|
1148 00e0: 4d a9 89 89 b4 9a 32 0c 43 46 86 87 a9 8d 41 9a |M.....2.CF....A.|
1149 00f0: 98 46 9a 0d 31 32 1a 34 0d 0c 8d a2 0c 98 4d 06 |.F..12.4......M.|
1150 0100: 8c 40 c2 60 8d 0d 0c 20 c9 89 fa a0 d0 d3 21 a1 |.@.`... ......!.|
1151 0110: ea 34 d3 68 9e a6 d1 74 05 33 cb 66 96 93 28 64 |.4.h...t.3.f..(d|
1152 0120: 40 91 22 ac 55 9b ea 40 7b 38 94 e2 f8 06 00 cb |@.".U..@{8......|
1153 0130: 28 02 00 4d ab 40 24 10 43 18 cf 64 b4 06 83 0c |(..M.@$.C..d....|
1154 0140: 34 6c b4 a3 d4 0a 0a e4 a8 5c 4e 23 c0 c9 7a 31 |4l.......\N#..z1|
1155 0150: 97 87 77 7a 64 88 80 8e 60 97 20 93 0f 8e eb c4 |..wzd...`. .....|
1156 0160: 62 a4 44 a3 52 20 b2 99 a9 2e e1 d7 29 4a 54 ac |b.D.R ......)JT.|
1157 0170: 44 7a bb cc 04 3d e0 aa bd 6a 33 5e 9b a2 57 36 |Dz...=...j3^..W6|
1158 0180: fa cb 45 bb 6d 3e c1 d9 d9 f5 83 69 8a d0 e0 e2 |..E.m>.....i....|
1159 0190: e7 ae 90 55 24 da 3f ab 78 c0 4c b4 56 a3 9e a4 |...U$.?.x.L.V...|
1160 01a0: af 9c 65 74 86 ec 6d dc 62 dc 33 ca c8 50 dd 9d |..et..m.b.3..P..|
1161 01b0: 98 8e 9e 59 20 f3 f0 42 91 4a 09 f5 75 8d 3d a5 |...Y ..B.J..u.=.|
1162 01c0: a5 15 cb 8d 10 63 b0 c2 2e b2 81 f7 c1 76 0e 53 |.....c.......v.S|
1163 01d0: 6c 0e 46 73 b5 ae 67 f9 4c 0b 45 6b a8 32 2a 2f |l.Fs..g.L.Ek.2*/|
1164 01e0: a2 54 a4 44 05 20 a1 38 d1 a4 c6 09 a8 2b 08 99 |.T.D. .8.....+..|
1165 01f0: a4 14 ae 8d a3 e3 aa 34 27 d8 44 ca c3 5d 21 8b |.......4'.D..]!.|
1166 0200: 1a 1e 97 29 71 2b 09 4a 4a 55 55 94 58 65 b2 bc |...)q+.JJUU.Xe..|
1167 0210: f3 a5 90 26 36 76 67 7a 51 98 d6 8a 4a 99 50 b5 |...&6vgzQ...J.P.|
1168 0220: 99 8f 94 21 17 a9 8b f3 ad 4c 33 d4 2e 40 c8 0c |...!.....L3..@..|
1169 0230: 3b 90 53 39 db 48 02 34 83 48 d6 b3 99 13 d2 58 |;.S9.H.4.H.....X|
1170 0240: 65 8e 71 ac a9 06 95 f2 c4 8e b4 08 6b d3 0c ae |e.q.........k...|
1171 0250: d9 90 56 71 43 a7 a2 62 16 3e 50 63 d3 57 3c 2d |..VqC..b.>Pc.W<-|
1172 0260: 9f 0f 34 05 08 d8 a6 4b 59 31 54 66 3a 45 0c 8a |..4....KY1Tf:E..|
1173 0270: c7 90 3a f0 6a 83 1b f5 ca fb 80 2b 50 06 fb 51 |..:.j......+P..Q|
1174 0280: 7e a6 a4 d4 81 44 82 21 54 00 5b 1a 30 83 62 a3 |~....D.!T.[.0.b.|
1175 0290: 18 b6 24 19 1e 45 df 4d 5c db a6 af 5b ac 90 fa |..$..E.M\...[...|
1176 02a0: 3e ed f9 ec 4c ba 36 ee d8 60 20 a7 c7 3b cb d1 |>...L.6..` ..;..|
1177 02b0: 90 43 7d 27 16 50 5d ad f4 14 07 0b 90 5c cc 6b |.C}'.P]......\.k|
1178 02c0: 8d 3f a6 88 f4 34 37 a8 cf 14 63 36 19 f7 3e 28 |.?...47...c6..>(|
1179 02d0: de 99 e8 16 a4 9d 0d 40 a1 a7 24 52 14 a6 72 62 |.......@..$R..rb|
1180 02e0: 59 5a ca 2d e5 51 90 78 88 d9 c6 c7 21 d0 f7 46 |YZ.-.Q.x....!..F|
1181 02f0: b2 04 46 44 4e 20 9c 12 b1 03 4e 25 e0 a9 0c 58 |..FDN ....N%...X|
1182 0300: 5b 1d 3c 93 20 01 51 de a9 1c 69 23 32 46 14 b4 |[.<. .Q...i#2F..|
1183 0310: 90 db 17 98 98 50 03 90 29 aa 40 b0 13 d8 43 d2 |.....P..).@...C.|
1184 0320: 5f c5 9d eb f3 f2 ad 41 e8 7a a9 ed a1 58 84 a6 |_......A.z...X..|
1185 0330: 42 bf d6 fc 24 82 c1 20 32 26 4a 15 a6 1d 29 7f |B...$.. 2&J...).|
1186 0340: 7e f4 3d 07 bc 62 9a 5b ec 44 3d 72 1d 41 8b 5c |~.=..b.[.D=r.A.\|
1187 0350: 80 de 0e 62 9a 2e f8 83 00 d5 07 a0 9c c6 74 98 |...b..........t.|
1188 0360: 11 b2 5e a9 38 02 03 ee fd 86 5c f4 86 b3 ae da |..^.8.....\.....|
1189 0370: 05 94 01 c5 c6 ea 18 e6 ba 2a ba b3 04 5c 96 89 |.........*...\..|
1190 0380: 72 63 5b 10 11 f6 67 34 98 cb e4 c0 4e fa e6 99 |rc[...g4....N...|
1191 0390: 19 6e 50 e8 26 8d 0c 17 e0 be ef e1 8e 02 6f 32 |.nP.&.........o2|
1192 03a0: 82 dc 26 f8 a1 08 f3 8a 0d f3 c4 75 00 48 73 b8 |..&........u.Hs.|
1193 03b0: be 3b 0d 7f d0 fd c7 78 96 ec e0 03 80 68 4d 8d |.;.....x.....hM.|
1194 03c0: 43 8c d7 68 58 f9 50 f0 18 cb 21 58 1b 60 cd 1f |C..hX.P...!X.`..|
1195 03d0: 84 36 2e 16 1f 0a f7 4e 8f eb df 01 2d c2 79 0b |.6.....N....-.y.|
1196 03e0: f7 24 ea 0d e8 59 86 51 6e 1c 30 a3 ad 2f ee 8c |.$...Y.Qn.0../..|
1197 03f0: 90 c8 84 d5 e8 34 c1 95 b2 c9 f6 4d 87 1c 7d 19 |.....4.....M..}.|
1198 0400: d6 41 58 56 7a e0 6c ba 10 c7 e8 33 39 36 96 e7 |.AXVz.l....396..|
1199 0410: d2 f9 59 9a 08 95 48 38 e7 0b b7 0a 24 67 c4 39 |..Y...H8....$g.9|
1200 0420: 8b 43 88 57 9c 01 f5 61 b5 e1 27 41 7e af 83 fe |.C.W...a..'A~...|
1201 0430: 2e e4 8a 70 a1 21 46 96 30 7a |...p.!F.0z|
1202 $ hg debugbundle ../rev.hg2.bz
1203 Stream params: {'Compression': 'BZ'}
1204 changegroup -- '{}'
1205 32af7686d403cf45b5d95f2d70cebea587ac806a
1206 9520eea781bcca16c1e15acc0ba14335a0e8e5ba
1207 eea13746799a9e0bfd88f29d3c2e9dc9389f524f
1208 02de42196ebee42ef284b6780a87cdc96e8eaab6
1209 $ hg unbundle ../rev.hg2.bz
1210 adding changesets
1211 adding manifests
1212 adding file changes
1213 added 0 changesets with 0 changes to 3 files
1214
1215 unknown compression while unbundling
1216 -----------------------------
1217
1218 $ hg bundle2 --param Compression=FooBarUnknown --rev '8+7+5+4' ../rev.hg2.bz
1219 $ cat ../rev.hg2.bz | hg statbundle2
1220 abort: unknown parameters: Stream Parameter - Compression='FooBarUnknown'
1221 [255]
1047
1222
1048 $ cd ..
1223 $ cd ..
General Comments 0
You need to be logged in to leave comments. Login now