##// END OF EJS Templates
bundle2: record processing results in the bundleoperation object...
Pierre-Yves David -
r20949:571f2903 default
parent child Browse files
Show More
@@ -1,486 +1,526 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: (16 bits integer)
34 :params size: (16 bits integer)
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. parameter with value
43 The blob contains a space separated list of parameters. parameter 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 safefly ignored. However when the first
49 parameter is advisory and can be safefly 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 remains simple and we want to discourage any
55 - Stream level parameters should remains simple and we want to discourage any
56 crazy usage.
56 crazy usage.
57 - Textual data allow easy human inspection of a the bundle2 header in case of
57 - Textual data allow easy human inspection of a the 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: (16 bits inter)
67 :header size: (16 bits inter)
68
68
69 The total number of Bytes used by the part headers. When the header is empty
69 The total number of Bytes used by the part headers. 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 :typename: alphanumerical part name
88 :typename: alphanumerical part name
89
89
90 :parameters:
90 :parameters:
91
91
92 Part's parameter may have arbitraty content, the binary structure is::
92 Part's parameter may have arbitraty content, the binary structure is::
93
93
94 <mandatory-count><advisory-count><param-sizes><param-data>
94 <mandatory-count><advisory-count><param-sizes><param-data>
95
95
96 :mandatory-count: 1 byte, number of mandatory parameters
96 :mandatory-count: 1 byte, number of mandatory parameters
97
97
98 :advisory-count: 1 byte, number of advisory parameters
98 :advisory-count: 1 byte, number of advisory parameters
99
99
100 :param-sizes:
100 :param-sizes:
101
101
102 N couple of bytes, where N is the total number of parameters. Each
102 N couple of bytes, where N is the total number of parameters. Each
103 couple contains (<size-of-key>, <size-of-value) for one parameter.
103 couple contains (<size-of-key>, <size-of-value) for one parameter.
104
104
105 :param-data:
105 :param-data:
106
106
107 A blob of bytes from which each parameter key and value can be
107 A blob of bytes from which each parameter key and value can be
108 retrieved using the list of size couples stored in the previous
108 retrieved using the list of size couples stored in the previous
109 field.
109 field.
110
110
111 Mandatory parameters comes first, then the advisory ones.
111 Mandatory parameters comes first, then the advisory ones.
112
112
113 :payload:
113 :payload:
114
114
115 payload is a series of `<chunksize><chunkdata>`.
115 payload is a series of `<chunksize><chunkdata>`.
116
116
117 `chunksize` is a 32 bits integer, `chunkdata` are plain bytes (as much as
117 `chunksize` is a 32 bits integer, `chunkdata` are plain bytes (as much as
118 `chunksize` says)` The payload part is concluded by a zero size chunk.
118 `chunksize` says)` The payload part is concluded by a zero size chunk.
119
119
120 The current implementation always produces either zero or one chunk.
120 The current implementation always produces either zero or one chunk.
121 This is an implementation limitation that will ultimatly be lifted.
121 This is an implementation limitation that will ultimatly be lifted.
122
122
123 Bundle processing
123 Bundle processing
124 ============================
124 ============================
125
125
126 Each part is processed in order using a "part handler". Handler are registered
126 Each part is processed in order using a "part handler". Handler are registered
127 for a certain part type.
127 for a certain part type.
128
128
129 The matching of a part to its handler is case insensitive. The case of the
129 The matching of a part to its handler is case insensitive. The case of the
130 part type is used to know if a part is mandatory or advisory. If the Part type
130 part type is used to know if a part is mandatory or advisory. If the Part type
131 contains any uppercase char it is considered mandatory. When no handler is
131 contains any uppercase char it is considered mandatory. When no handler is
132 known for a Mandatory part, the process is aborted and an exception is raised.
132 known for a Mandatory part, the process is aborted and an exception is raised.
133 If the part is advisory and no handler is known, the part is ignored. When the
133 If the part is advisory and no handler is known, the part is ignored. When the
134 process is aborted, the full bundle is still read from the stream to keep the
134 process is aborted, the full bundle is still read from the stream to keep the
135 channel usable. But none of the part read from an abort are processed. In the
135 channel usable. But none of the part read from an abort are processed. In the
136 future, dropping the stream may become an option for channel we do not care to
136 future, dropping the stream may become an option for channel we do not care to
137 preserve.
137 preserve.
138 """
138 """
139
139
140 import util
140 import util
141 import struct
141 import struct
142 import urllib
142 import urllib
143 import string
143 import string
144
144
145 import changegroup
145 import changegroup
146 from i18n import _
146 from i18n import _
147
147
148 _pack = struct.pack
148 _pack = struct.pack
149 _unpack = struct.unpack
149 _unpack = struct.unpack
150
150
151 _magicstring = 'HG20'
151 _magicstring = 'HG20'
152
152
153 _fstreamparamsize = '>H'
153 _fstreamparamsize = '>H'
154 _fpartheadersize = '>H'
154 _fpartheadersize = '>H'
155 _fparttypesize = '>B'
155 _fparttypesize = '>B'
156 _fpayloadsize = '>I'
156 _fpayloadsize = '>I'
157 _fpartparamcount = '>BB'
157 _fpartparamcount = '>BB'
158
158
159 def _makefpartparamsizes(nbparams):
159 def _makefpartparamsizes(nbparams):
160 """return a struct format to read part parameter sizes
160 """return a struct format to read part parameter sizes
161
161
162 The number parameters is variable so we need to build that format
162 The number parameters is variable so we need to build that format
163 dynamically.
163 dynamically.
164 """
164 """
165 return '>'+('BB'*nbparams)
165 return '>'+('BB'*nbparams)
166
166
167 parthandlermapping = {}
167 parthandlermapping = {}
168
168
169 def parthandler(parttype):
169 def parthandler(parttype):
170 """decorator that register a function as a bundle2 part handler
170 """decorator that register a function as a bundle2 part handler
171
171
172 eg::
172 eg::
173
173
174 @parthandler('myparttype')
174 @parthandler('myparttype')
175 def myparttypehandler(...):
175 def myparttypehandler(...):
176 '''process a part of type "my part".'''
176 '''process a part of type "my part".'''
177 ...
177 ...
178 """
178 """
179 def _decorator(func):
179 def _decorator(func):
180 lparttype = parttype.lower() # enforce lower case matching.
180 lparttype = parttype.lower() # enforce lower case matching.
181 assert lparttype not in parthandlermapping
181 assert lparttype not in parthandlermapping
182 parthandlermapping[lparttype] = func
182 parthandlermapping[lparttype] = func
183 return func
183 return func
184 return _decorator
184 return _decorator
185
185
186 class unbundlerecords(object):
187 """keep record of what happens during and unbundle
188
189 New records are added using `records.add('cat', obj)`. Where 'cat' is a
190 category of record and obj is an arbitraty object.
191
192 `records['cat']` will return all entries of this category 'cat'.
193
194 Iterating on the object itself will yield `('category', obj)` tuples
195 for all entries.
196
197 All iterations happens in chronological order.
198 """
199
200 def __init__(self):
201 self._categories = {}
202 self._sequences = []
203
204 def add(self, category, entry):
205 """add a new record of a given category.
206
207 The entry can then be retrieved in the list returned by
208 self['category']."""
209 self._categories.setdefault(category, []).append(entry)
210 self._sequences.append((category, entry))
211
212 def __getitem__(self, cat):
213 return tuple(self._categories.get(cat, ()))
214
215 def __iter__(self):
216 return iter(self._sequences)
217
218 def __len__(self):
219 return len(self._sequences)
220
221 def __nonzero__(self):
222 return bool(self._sequences)
223
186 class bundleoperation(object):
224 class bundleoperation(object):
187 """an object that represents a single bundling process
225 """an object that represents a single bundling process
188
226
189 Its purpose is to carry unbundle-related objects and states.
227 Its purpose is to carry unbundle-related objects and states.
190
228
191 A new object should be created at the beginning of each bundle processing.
229 A new object should be created at the beginning of each bundle processing.
192 The object is to be returned by the processing function.
230 The object is to be returned by the processing function.
193
231
194 The object has very little content now it will ultimately contain:
232 The object has very little content now it will ultimately contain:
195 * an access to the repo the bundle is applied to,
233 * an access to the repo the bundle is applied to,
196 * a ui object,
234 * a ui object,
197 * a way to retrieve a transaction to add changes to the repo,
235 * a way to retrieve a transaction to add changes to the repo,
198 * a way to record the result of processing each part,
236 * a way to record the result of processing each part,
199 * a way to construct a bundle response when applicable.
237 * a way to construct a bundle response when applicable.
200 """
238 """
201
239
202 def __init__(self, repo):
240 def __init__(self, repo):
203 self.repo = repo
241 self.repo = repo
204 self.ui = repo.ui
242 self.ui = repo.ui
243 self.records = unbundlerecords()
205
244
206 def processbundle(repo, unbundler):
245 def processbundle(repo, unbundler):
207 """This function process a bundle, apply effect to/from a repo
246 """This function process a bundle, apply effect to/from a repo
208
247
209 It iterates over each part then searches for and uses the proper handling
248 It iterates over each part then searches for and uses the proper handling
210 code to process the part. Parts are processed in order.
249 code to process the part. Parts are processed in order.
211
250
212 This is very early version of this function that will be strongly reworked
251 This is very early version of this function that will be strongly reworked
213 before final usage.
252 before final usage.
214
253
215 Unknown Mandatory part will abort the process.
254 Unknown Mandatory part will abort the process.
216 """
255 """
217 op = bundleoperation(repo)
256 op = bundleoperation(repo)
218 # todo:
257 # todo:
219 # - replace this is a init function soon.
258 # - replace this is a init function soon.
220 # - exception catching
259 # - exception catching
221 unbundler.params
260 unbundler.params
222 iterparts = iter(unbundler)
261 iterparts = iter(unbundler)
223 try:
262 try:
224 for part in iterparts:
263 for part in iterparts:
225 parttype = part.type
264 parttype = part.type
226 # part key are matched lower case
265 # part key are matched lower case
227 key = parttype.lower()
266 key = parttype.lower()
228 try:
267 try:
229 handler = parthandlermapping[key]
268 handler = parthandlermapping[key]
230 op.ui.debug('found a handler for part %r\n' % parttype)
269 op.ui.debug('found a handler for part %r\n' % parttype)
231 except KeyError:
270 except KeyError:
232 if key != parttype: # mandatory parts
271 if key != parttype: # mandatory parts
233 # todo:
272 # todo:
234 # - use a more precise exception
273 # - use a more precise exception
235 raise
274 raise
236 op.ui.debug('ignoring unknown advisory part %r\n' % key)
275 op.ui.debug('ignoring unknown advisory part %r\n' % key)
237 # todo:
276 # todo:
238 # - consume the part once we use streaming
277 # - consume the part once we use streaming
239 continue
278 continue
240 handler(op, part)
279 handler(op, part)
241 except Exception:
280 except Exception:
242 for part in iterparts:
281 for part in iterparts:
243 pass # consume the bundle content
282 pass # consume the bundle content
244 raise
283 raise
284 return op
245
285
246 class bundle20(object):
286 class bundle20(object):
247 """represent an outgoing bundle2 container
287 """represent an outgoing bundle2 container
248
288
249 Use the `addparam` method to add stream level parameter. and `addpart` to
289 Use the `addparam` method to add stream level parameter. and `addpart` to
250 populate it. Then call `getchunks` to retrieve all the binary chunks of
290 populate it. Then call `getchunks` to retrieve all the binary chunks of
251 datathat compose the bundle2 container."""
291 datathat compose the bundle2 container."""
252
292
253 def __init__(self, ui):
293 def __init__(self, ui):
254 self.ui = ui
294 self.ui = ui
255 self._params = []
295 self._params = []
256 self._parts = []
296 self._parts = []
257
297
258 def addparam(self, name, value=None):
298 def addparam(self, name, value=None):
259 """add a stream level parameter"""
299 """add a stream level parameter"""
260 if not name:
300 if not name:
261 raise ValueError('empty parameter name')
301 raise ValueError('empty parameter name')
262 if name[0] not in string.letters:
302 if name[0] not in string.letters:
263 raise ValueError('non letter first character: %r' % name)
303 raise ValueError('non letter first character: %r' % name)
264 self._params.append((name, value))
304 self._params.append((name, value))
265
305
266 def addpart(self, part):
306 def addpart(self, part):
267 """add a new part to the bundle2 container
307 """add a new part to the bundle2 container
268
308
269 Parts contains the actuall applicative payload."""
309 Parts contains the actuall applicative payload."""
270 self._parts.append(part)
310 self._parts.append(part)
271
311
272 def getchunks(self):
312 def getchunks(self):
273 self.ui.debug('start emission of %s stream\n' % _magicstring)
313 self.ui.debug('start emission of %s stream\n' % _magicstring)
274 yield _magicstring
314 yield _magicstring
275 param = self._paramchunk()
315 param = self._paramchunk()
276 self.ui.debug('bundle parameter: %s\n' % param)
316 self.ui.debug('bundle parameter: %s\n' % param)
277 yield _pack(_fstreamparamsize, len(param))
317 yield _pack(_fstreamparamsize, len(param))
278 if param:
318 if param:
279 yield param
319 yield param
280
320
281 self.ui.debug('start of parts\n')
321 self.ui.debug('start of parts\n')
282 for part in self._parts:
322 for part in self._parts:
283 self.ui.debug('bundle part: "%s"\n' % part.type)
323 self.ui.debug('bundle part: "%s"\n' % part.type)
284 for chunk in part.getchunks():
324 for chunk in part.getchunks():
285 yield chunk
325 yield chunk
286 self.ui.debug('end of bundle\n')
326 self.ui.debug('end of bundle\n')
287 yield '\0\0'
327 yield '\0\0'
288
328
289 def _paramchunk(self):
329 def _paramchunk(self):
290 """return a encoded version of all stream parameters"""
330 """return a encoded version of all stream parameters"""
291 blocks = []
331 blocks = []
292 for par, value in self._params:
332 for par, value in self._params:
293 par = urllib.quote(par)
333 par = urllib.quote(par)
294 if value is not None:
334 if value is not None:
295 value = urllib.quote(value)
335 value = urllib.quote(value)
296 par = '%s=%s' % (par, value)
336 par = '%s=%s' % (par, value)
297 blocks.append(par)
337 blocks.append(par)
298 return ' '.join(blocks)
338 return ' '.join(blocks)
299
339
300 class unbundle20(object):
340 class unbundle20(object):
301 """interpret a bundle2 stream
341 """interpret a bundle2 stream
302
342
303 (this will eventually yield parts)"""
343 (this will eventually yield parts)"""
304
344
305 def __init__(self, ui, fp):
345 def __init__(self, ui, fp):
306 self.ui = ui
346 self.ui = ui
307 self._fp = fp
347 self._fp = fp
308 header = self._readexact(4)
348 header = self._readexact(4)
309 magic, version = header[0:2], header[2:4]
349 magic, version = header[0:2], header[2:4]
310 if magic != 'HG':
350 if magic != 'HG':
311 raise util.Abort(_('not a Mercurial bundle'))
351 raise util.Abort(_('not a Mercurial bundle'))
312 if version != '20':
352 if version != '20':
313 raise util.Abort(_('unknown bundle version %s') % version)
353 raise util.Abort(_('unknown bundle version %s') % version)
314 self.ui.debug('start processing of %s stream\n' % header)
354 self.ui.debug('start processing of %s stream\n' % header)
315
355
316 def _unpack(self, format):
356 def _unpack(self, format):
317 """unpack this struct format from the stream"""
357 """unpack this struct format from the stream"""
318 data = self._readexact(struct.calcsize(format))
358 data = self._readexact(struct.calcsize(format))
319 return _unpack(format, data)
359 return _unpack(format, data)
320
360
321 def _readexact(self, size):
361 def _readexact(self, size):
322 """read exactly <size> bytes from the stream"""
362 """read exactly <size> bytes from the stream"""
323 return changegroup.readexactly(self._fp, size)
363 return changegroup.readexactly(self._fp, size)
324
364
325 @util.propertycache
365 @util.propertycache
326 def params(self):
366 def params(self):
327 """dictionnary of stream level parameters"""
367 """dictionnary of stream level parameters"""
328 self.ui.debug('reading bundle2 stream parameters\n')
368 self.ui.debug('reading bundle2 stream parameters\n')
329 params = {}
369 params = {}
330 paramssize = self._unpack(_fstreamparamsize)[0]
370 paramssize = self._unpack(_fstreamparamsize)[0]
331 if paramssize:
371 if paramssize:
332 for p in self._readexact(paramssize).split(' '):
372 for p in self._readexact(paramssize).split(' '):
333 p = p.split('=', 1)
373 p = p.split('=', 1)
334 p = [urllib.unquote(i) for i in p]
374 p = [urllib.unquote(i) for i in p]
335 if len(p) < 2:
375 if len(p) < 2:
336 p.append(None)
376 p.append(None)
337 self._processparam(*p)
377 self._processparam(*p)
338 params[p[0]] = p[1]
378 params[p[0]] = p[1]
339 return params
379 return params
340
380
341 def _processparam(self, name, value):
381 def _processparam(self, name, value):
342 """process a parameter, applying its effect if needed
382 """process a parameter, applying its effect if needed
343
383
344 Parameter starting with a lower case letter are advisory and will be
384 Parameter starting with a lower case letter are advisory and will be
345 ignored when unknown. Those starting with an upper case letter are
385 ignored when unknown. Those starting with an upper case letter are
346 mandatory and will this function will raise a KeyError when unknown.
386 mandatory and will this function will raise a KeyError when unknown.
347
387
348 Note: no option are currently supported. Any input will be either
388 Note: no option are currently supported. Any input will be either
349 ignored or failing.
389 ignored or failing.
350 """
390 """
351 if not name:
391 if not name:
352 raise ValueError('empty parameter name')
392 raise ValueError('empty parameter name')
353 if name[0] not in string.letters:
393 if name[0] not in string.letters:
354 raise ValueError('non letter first character: %r' % name)
394 raise ValueError('non letter first character: %r' % name)
355 # Some logic will be later added here to try to process the option for
395 # Some logic will be later added here to try to process the option for
356 # a dict of known parameter.
396 # a dict of known parameter.
357 if name[0].islower():
397 if name[0].islower():
358 self.ui.debug("ignoring unknown parameter %r\n" % name)
398 self.ui.debug("ignoring unknown parameter %r\n" % name)
359 else:
399 else:
360 raise KeyError(name)
400 raise KeyError(name)
361
401
362
402
363 def __iter__(self):
403 def __iter__(self):
364 """yield all parts contained in the stream"""
404 """yield all parts contained in the stream"""
365 # make sure param have been loaded
405 # make sure param have been loaded
366 self.params
406 self.params
367 self.ui.debug('start extraction of bundle2 parts\n')
407 self.ui.debug('start extraction of bundle2 parts\n')
368 part = self._readpart()
408 part = self._readpart()
369 while part is not None:
409 while part is not None:
370 yield part
410 yield part
371 part = self._readpart()
411 part = self._readpart()
372 self.ui.debug('end of bundle2 stream\n')
412 self.ui.debug('end of bundle2 stream\n')
373
413
374 def _readpart(self):
414 def _readpart(self):
375 """return None when an end of stream markers is reach"""
415 """return None when an end of stream markers is reach"""
376
416
377 headersize = self._unpack(_fpartheadersize)[0]
417 headersize = self._unpack(_fpartheadersize)[0]
378 self.ui.debug('part header size: %i\n' % headersize)
418 self.ui.debug('part header size: %i\n' % headersize)
379 if not headersize:
419 if not headersize:
380 return None
420 return None
381 headerblock = self._readexact(headersize)
421 headerblock = self._readexact(headersize)
382 # some utility to help reading from the header block
422 # some utility to help reading from the header block
383 self._offset = 0 # layer violation to have something easy to understand
423 self._offset = 0 # layer violation to have something easy to understand
384 def fromheader(size):
424 def fromheader(size):
385 """return the next <size> byte from the header"""
425 """return the next <size> byte from the header"""
386 offset = self._offset
426 offset = self._offset
387 data = headerblock[offset:(offset + size)]
427 data = headerblock[offset:(offset + size)]
388 self._offset = offset + size
428 self._offset = offset + size
389 return data
429 return data
390 def unpackheader(format):
430 def unpackheader(format):
391 """read given format from header
431 """read given format from header
392
432
393 This automatically compute the size of the format to read."""
433 This automatically compute the size of the format to read."""
394 data = fromheader(struct.calcsize(format))
434 data = fromheader(struct.calcsize(format))
395 return _unpack(format, data)
435 return _unpack(format, data)
396
436
397 typesize = unpackheader(_fparttypesize)[0]
437 typesize = unpackheader(_fparttypesize)[0]
398 parttype = fromheader(typesize)
438 parttype = fromheader(typesize)
399 self.ui.debug('part type: "%s"\n' % parttype)
439 self.ui.debug('part type: "%s"\n' % parttype)
400 ## reading parameters
440 ## reading parameters
401 # param count
441 # param count
402 mancount, advcount = unpackheader(_fpartparamcount)
442 mancount, advcount = unpackheader(_fpartparamcount)
403 self.ui.debug('part parameters: %i\n' % (mancount + advcount))
443 self.ui.debug('part parameters: %i\n' % (mancount + advcount))
404 # param size
444 # param size
405 paramsizes = unpackheader(_makefpartparamsizes(mancount + advcount))
445 paramsizes = unpackheader(_makefpartparamsizes(mancount + advcount))
406 # make it a list of couple again
446 # make it a list of couple again
407 paramsizes = zip(paramsizes[::2], paramsizes[1::2])
447 paramsizes = zip(paramsizes[::2], paramsizes[1::2])
408 # split mandatory from advisory
448 # split mandatory from advisory
409 mansizes = paramsizes[:mancount]
449 mansizes = paramsizes[:mancount]
410 advsizes = paramsizes[mancount:]
450 advsizes = paramsizes[mancount:]
411 # retrive param value
451 # retrive param value
412 manparams = []
452 manparams = []
413 for key, value in mansizes:
453 for key, value in mansizes:
414 manparams.append((fromheader(key), fromheader(value)))
454 manparams.append((fromheader(key), fromheader(value)))
415 advparams = []
455 advparams = []
416 for key, value in advsizes:
456 for key, value in advsizes:
417 advparams.append((fromheader(key), fromheader(value)))
457 advparams.append((fromheader(key), fromheader(value)))
418 del self._offset # clean up layer, nobody saw anything.
458 del self._offset # clean up layer, nobody saw anything.
419 ## part payload
459 ## part payload
420 payload = []
460 payload = []
421 payloadsize = self._unpack(_fpayloadsize)[0]
461 payloadsize = self._unpack(_fpayloadsize)[0]
422 self.ui.debug('payload chunk size: %i\n' % payloadsize)
462 self.ui.debug('payload chunk size: %i\n' % payloadsize)
423 while payloadsize:
463 while payloadsize:
424 payload.append(self._readexact(payloadsize))
464 payload.append(self._readexact(payloadsize))
425 payloadsize = self._unpack(_fpayloadsize)[0]
465 payloadsize = self._unpack(_fpayloadsize)[0]
426 self.ui.debug('payload chunk size: %i\n' % payloadsize)
466 self.ui.debug('payload chunk size: %i\n' % payloadsize)
427 payload = ''.join(payload)
467 payload = ''.join(payload)
428 current = part(parttype, manparams, advparams, data=payload)
468 current = part(parttype, manparams, advparams, data=payload)
429 return current
469 return current
430
470
431
471
432 class part(object):
472 class part(object):
433 """A bundle2 part contains application level payload
473 """A bundle2 part contains application level payload
434
474
435 The part `type` is used to route the part to the application level
475 The part `type` is used to route the part to the application level
436 handler.
476 handler.
437 """
477 """
438
478
439 def __init__(self, parttype, mandatoryparams=(), advisoryparams=(),
479 def __init__(self, parttype, mandatoryparams=(), advisoryparams=(),
440 data=''):
480 data=''):
441 self.type = parttype
481 self.type = parttype
442 self.data = data
482 self.data = data
443 self.mandatoryparams = mandatoryparams
483 self.mandatoryparams = mandatoryparams
444 self.advisoryparams = advisoryparams
484 self.advisoryparams = advisoryparams
445
485
446 def getchunks(self):
486 def getchunks(self):
447 #### header
487 #### header
448 ## parttype
488 ## parttype
449 header = [_pack(_fparttypesize, len(self.type)),
489 header = [_pack(_fparttypesize, len(self.type)),
450 self.type,
490 self.type,
451 ]
491 ]
452 ## parameters
492 ## parameters
453 # count
493 # count
454 manpar = self.mandatoryparams
494 manpar = self.mandatoryparams
455 advpar = self.advisoryparams
495 advpar = self.advisoryparams
456 header.append(_pack(_fpartparamcount, len(manpar), len(advpar)))
496 header.append(_pack(_fpartparamcount, len(manpar), len(advpar)))
457 # size
497 # size
458 parsizes = []
498 parsizes = []
459 for key, value in manpar:
499 for key, value in manpar:
460 parsizes.append(len(key))
500 parsizes.append(len(key))
461 parsizes.append(len(value))
501 parsizes.append(len(value))
462 for key, value in advpar:
502 for key, value in advpar:
463 parsizes.append(len(key))
503 parsizes.append(len(key))
464 parsizes.append(len(value))
504 parsizes.append(len(value))
465 paramsizes = _pack(_makefpartparamsizes(len(parsizes) / 2), *parsizes)
505 paramsizes = _pack(_makefpartparamsizes(len(parsizes) / 2), *parsizes)
466 header.append(paramsizes)
506 header.append(paramsizes)
467 # key, value
507 # key, value
468 for key, value in manpar:
508 for key, value in manpar:
469 header.append(key)
509 header.append(key)
470 header.append(value)
510 header.append(value)
471 for key, value in advpar:
511 for key, value in advpar:
472 header.append(key)
512 header.append(key)
473 header.append(value)
513 header.append(value)
474 ## finalize header
514 ## finalize header
475 headerchunk = ''.join(header)
515 headerchunk = ''.join(header)
476 yield _pack(_fpartheadersize, len(headerchunk))
516 yield _pack(_fpartheadersize, len(headerchunk))
477 yield headerchunk
517 yield headerchunk
478 ## payload
518 ## payload
479 # we only support fixed size data now.
519 # we only support fixed size data now.
480 # This will be improved in the future.
520 # This will be improved in the future.
481 if len(self.data):
521 if len(self.data):
482 yield _pack(_fpayloadsize, len(self.data))
522 yield _pack(_fpayloadsize, len(self.data))
483 yield self.data
523 yield self.data
484 # end of payload
524 # end of payload
485 yield _pack(_fpayloadsize, 0)
525 yield _pack(_fpayloadsize, 0)
486
526
@@ -1,407 +1,414 b''
1
1
2 Create an extension to test bundle2 API
2 Create an extension to test bundle2 API
3
3
4 $ cat > bundle2.py << EOF
4 $ cat > bundle2.py << EOF
5 > """A small extension to test bundle2 implementation
5 > """A small extension to test bundle2 implementation
6 >
6 >
7 > Current bundle2 implementation is far too limited to be used in any core
7 > Current bundle2 implementation is far too limited to be used in any core
8 > code. We still need to be able to test it while it grow up.
8 > code. We still need to be able to test it while it grow up.
9 > """
9 > """
10 >
10 >
11 > import sys
11 > import sys
12 > from mercurial import cmdutil
12 > from mercurial import cmdutil
13 > from mercurial import util
13 > from mercurial import util
14 > from mercurial import bundle2
14 > from mercurial import bundle2
15 > cmdtable = {}
15 > cmdtable = {}
16 > command = cmdutil.command(cmdtable)
16 > command = cmdutil.command(cmdtable)
17 >
17 >
18 > ELEPHANTSSONG = """Patali Dirapata, Cromda Cromda Ripalo, Pata Pata, Ko Ko Ko
18 > ELEPHANTSSONG = """Patali Dirapata, Cromda Cromda Ripalo, Pata Pata, Ko Ko Ko
19 > Bokoro Dipoulito, Rondi Rondi Pepino, Pata Pata, Ko Ko Ko
19 > Bokoro Dipoulito, Rondi Rondi Pepino, Pata Pata, Ko Ko Ko
20 > Emana Karassoli, Loucra Loucra Ponponto, Pata Pata, Ko Ko Ko."""
20 > Emana Karassoli, Loucra Loucra Ponponto, Pata Pata, Ko Ko Ko."""
21 > assert len(ELEPHANTSSONG) == 178 # future test say 178 bytes, trust it.
21 > assert len(ELEPHANTSSONG) == 178 # future test say 178 bytes, trust it.
22 >
22 >
23 > @bundle2.parthandler('test:song')
23 > @bundle2.parthandler('test:song')
24 > def songhandler(op, part):
24 > def songhandler(op, part):
25 > """handle a "test:song" bundle2 part, printing the lyrics on stdin"""
25 > """handle a "test:song" bundle2 part, printing the lyrics on stdin"""
26 > op.ui.write('The choir starts singing:\n')
26 > op.ui.write('The choir starts singing:\n')
27 > verses = 0
27 > for line in part.data.split('\n'):
28 > for line in part.data.split('\n'):
28 > op.ui.write(' %s\n' % line)
29 > op.ui.write(' %s\n' % line)
30 > verses += 1
31 > op.records.add('song', {'verses': verses})
29 >
32 >
30 > @command('bundle2',
33 > @command('bundle2',
31 > [('', 'param', [], 'stream level parameter'),
34 > [('', 'param', [], 'stream level parameter'),
32 > ('', 'unknown', False, 'include an unknown mandatory part in the bundle'),
35 > ('', 'unknown', False, 'include an unknown mandatory part in the bundle'),
33 > ('', 'parts', False, 'include some arbitrary parts to the bundle'),],
36 > ('', 'parts', False, 'include some arbitrary parts to the bundle'),],
34 > '[OUTPUTFILE]')
37 > '[OUTPUTFILE]')
35 > def cmdbundle2(ui, repo, path=None, **opts):
38 > def cmdbundle2(ui, repo, path=None, **opts):
36 > """write a bundle2 container on standard ouput"""
39 > """write a bundle2 container on standard ouput"""
37 > bundler = bundle2.bundle20(ui)
40 > bundler = bundle2.bundle20(ui)
38 > for p in opts['param']:
41 > for p in opts['param']:
39 > p = p.split('=', 1)
42 > p = p.split('=', 1)
40 > try:
43 > try:
41 > bundler.addparam(*p)
44 > bundler.addparam(*p)
42 > except ValueError, exc:
45 > except ValueError, exc:
43 > raise util.Abort('%s' % exc)
46 > raise util.Abort('%s' % exc)
44 >
47 >
45 > if opts['parts']:
48 > if opts['parts']:
46 > part = bundle2.part('test:empty')
49 > part = bundle2.part('test:empty')
47 > bundler.addpart(part)
50 > bundler.addpart(part)
48 > # add a second one to make sure we handle multiple parts
51 > # add a second one to make sure we handle multiple parts
49 > part = bundle2.part('test:empty')
52 > part = bundle2.part('test:empty')
50 > bundler.addpart(part)
53 > bundler.addpart(part)
51 > part = bundle2.part('test:song', data=ELEPHANTSSONG)
54 > part = bundle2.part('test:song', data=ELEPHANTSSONG)
52 > bundler.addpart(part)
55 > bundler.addpart(part)
53 > part = bundle2.part('test:math',
56 > part = bundle2.part('test:math',
54 > [('pi', '3.14'), ('e', '2.72')],
57 > [('pi', '3.14'), ('e', '2.72')],
55 > [('cooking', 'raw')],
58 > [('cooking', 'raw')],
56 > '42')
59 > '42')
57 > bundler.addpart(part)
60 > bundler.addpart(part)
58 > if opts['unknown']:
61 > if opts['unknown']:
59 > part = bundle2.part('test:UNKNOWN',
62 > part = bundle2.part('test:UNKNOWN',
60 > data='some random content')
63 > data='some random content')
61 > bundler.addpart(part)
64 > bundler.addpart(part)
62 >
65 >
63 > if path is None:
66 > if path is None:
64 > file = sys.stdout
67 > file = sys.stdout
65 > else:
68 > else:
66 > file = open(path, 'w')
69 > file = open(path, 'w')
67 >
70 >
68 > for chunk in bundler.getchunks():
71 > for chunk in bundler.getchunks():
69 > file.write(chunk)
72 > file.write(chunk)
70 >
73 >
71 > @command('unbundle2', [], '')
74 > @command('unbundle2', [], '')
72 > def cmdunbundle2(ui, repo):
75 > def cmdunbundle2(ui, repo):
73 > """process a bundle2 stream from stdin on the current repo"""
76 > """process a bundle2 stream from stdin on the current repo"""
74 > try:
77 > try:
75 > lock = repo.lock()
78 > lock = repo.lock()
76 > try:
79 > try:
77 > unbundler = bundle2.unbundle20(ui, sys.stdin)
80 > unbundler = bundle2.unbundle20(ui, sys.stdin)
78 > bundle2.processbundle(repo, unbundler)
81 > op = bundle2.processbundle(repo, unbundler)
79 > except KeyError, exc:
82 > except KeyError, exc:
80 > raise util.Abort('missing support for %s' % exc)
83 > raise util.Abort('missing support for %s' % exc)
81 > finally:
84 > finally:
82 > lock.release()
85 > lock.release()
83 > remains = sys.stdin.read()
86 > remains = sys.stdin.read()
84 > ui.write('%i unread bytes\n' % len(remains))
87 > ui.write('%i unread bytes\n' % len(remains))
88 > if op.records['song']:
89 > totalverses = sum(r['verses'] for r in op.records['song'])
90 > ui.write('%i total verses sung\n' % totalverses)
85 >
91 >
86 > @command('statbundle2', [], '')
92 > @command('statbundle2', [], '')
87 > def cmdstatbundle2(ui, repo):
93 > def cmdstatbundle2(ui, repo):
88 > """print statistic on the bundle2 container read from stdin"""
94 > """print statistic on the bundle2 container read from stdin"""
89 > unbundler = bundle2.unbundle20(ui, sys.stdin)
95 > unbundler = bundle2.unbundle20(ui, sys.stdin)
90 > try:
96 > try:
91 > params = unbundler.params
97 > params = unbundler.params
92 > except KeyError, exc:
98 > except KeyError, exc:
93 > raise util.Abort('unknown parameters: %s' % exc)
99 > raise util.Abort('unknown parameters: %s' % exc)
94 > ui.write('options count: %i\n' % len(params))
100 > ui.write('options count: %i\n' % len(params))
95 > for key in sorted(params):
101 > for key in sorted(params):
96 > ui.write('- %s\n' % key)
102 > ui.write('- %s\n' % key)
97 > value = params[key]
103 > value = params[key]
98 > if value is not None:
104 > if value is not None:
99 > ui.write(' %s\n' % value)
105 > ui.write(' %s\n' % value)
100 > parts = list(unbundler)
106 > parts = list(unbundler)
101 > ui.write('parts count: %i\n' % len(parts))
107 > ui.write('parts count: %i\n' % len(parts))
102 > for p in parts:
108 > for p in parts:
103 > ui.write(' :%s:\n' % p.type)
109 > ui.write(' :%s:\n' % p.type)
104 > ui.write(' mandatory: %i\n' % len(p.mandatoryparams))
110 > ui.write(' mandatory: %i\n' % len(p.mandatoryparams))
105 > ui.write(' advisory: %i\n' % len(p.advisoryparams))
111 > ui.write(' advisory: %i\n' % len(p.advisoryparams))
106 > ui.write(' payload: %i bytes\n' % len(p.data))
112 > ui.write(' payload: %i bytes\n' % len(p.data))
107 > EOF
113 > EOF
108 $ cat >> $HGRCPATH << EOF
114 $ cat >> $HGRCPATH << EOF
109 > [extensions]
115 > [extensions]
110 > bundle2=$TESTTMP/bundle2.py
116 > bundle2=$TESTTMP/bundle2.py
111 > EOF
117 > EOF
112
118
113 The extension requires a repo (currently unused)
119 The extension requires a repo (currently unused)
114
120
115 $ hg init main
121 $ hg init main
116 $ cd main
122 $ cd main
117 $ touch a
123 $ touch a
118 $ hg add a
124 $ hg add a
119 $ hg commit -m 'a'
125 $ hg commit -m 'a'
120
126
121
127
122 Empty bundle
128 Empty bundle
123 =================
129 =================
124
130
125 - no option
131 - no option
126 - no parts
132 - no parts
127
133
128 Test bundling
134 Test bundling
129
135
130 $ hg bundle2
136 $ hg bundle2
131 HG20\x00\x00\x00\x00 (no-eol) (esc)
137 HG20\x00\x00\x00\x00 (no-eol) (esc)
132
138
133 Test unbundling
139 Test unbundling
134
140
135 $ hg bundle2 | hg statbundle2
141 $ hg bundle2 | hg statbundle2
136 options count: 0
142 options count: 0
137 parts count: 0
143 parts count: 0
138
144
139 Test old style bundle are detected and refused
145 Test old style bundle are detected and refused
140
146
141 $ hg bundle --all ../bundle.hg
147 $ hg bundle --all ../bundle.hg
142 1 changesets found
148 1 changesets found
143 $ hg statbundle2 < ../bundle.hg
149 $ hg statbundle2 < ../bundle.hg
144 abort: unknown bundle version 10
150 abort: unknown bundle version 10
145 [255]
151 [255]
146
152
147 Test parameters
153 Test parameters
148 =================
154 =================
149
155
150 - some options
156 - some options
151 - no parts
157 - no parts
152
158
153 advisory parameters, no value
159 advisory parameters, no value
154 -------------------------------
160 -------------------------------
155
161
156 Simplest possible parameters form
162 Simplest possible parameters form
157
163
158 Test generation simple option
164 Test generation simple option
159
165
160 $ hg bundle2 --param 'caution'
166 $ hg bundle2 --param 'caution'
161 HG20\x00\x07caution\x00\x00 (no-eol) (esc)
167 HG20\x00\x07caution\x00\x00 (no-eol) (esc)
162
168
163 Test unbundling
169 Test unbundling
164
170
165 $ hg bundle2 --param 'caution' | hg statbundle2
171 $ hg bundle2 --param 'caution' | hg statbundle2
166 options count: 1
172 options count: 1
167 - caution
173 - caution
168 parts count: 0
174 parts count: 0
169
175
170 Test generation multiple option
176 Test generation multiple option
171
177
172 $ hg bundle2 --param 'caution' --param 'meal'
178 $ hg bundle2 --param 'caution' --param 'meal'
173 HG20\x00\x0ccaution meal\x00\x00 (no-eol) (esc)
179 HG20\x00\x0ccaution meal\x00\x00 (no-eol) (esc)
174
180
175 Test unbundling
181 Test unbundling
176
182
177 $ hg bundle2 --param 'caution' --param 'meal' | hg statbundle2
183 $ hg bundle2 --param 'caution' --param 'meal' | hg statbundle2
178 options count: 2
184 options count: 2
179 - caution
185 - caution
180 - meal
186 - meal
181 parts count: 0
187 parts count: 0
182
188
183 advisory parameters, with value
189 advisory parameters, with value
184 -------------------------------
190 -------------------------------
185
191
186 Test generation
192 Test generation
187
193
188 $ hg bundle2 --param 'caution' --param 'meal=vegan' --param 'elephants'
194 $ hg bundle2 --param 'caution' --param 'meal=vegan' --param 'elephants'
189 HG20\x00\x1ccaution meal=vegan elephants\x00\x00 (no-eol) (esc)
195 HG20\x00\x1ccaution meal=vegan elephants\x00\x00 (no-eol) (esc)
190
196
191 Test unbundling
197 Test unbundling
192
198
193 $ hg bundle2 --param 'caution' --param 'meal=vegan' --param 'elephants' | hg statbundle2
199 $ hg bundle2 --param 'caution' --param 'meal=vegan' --param 'elephants' | hg statbundle2
194 options count: 3
200 options count: 3
195 - caution
201 - caution
196 - elephants
202 - elephants
197 - meal
203 - meal
198 vegan
204 vegan
199 parts count: 0
205 parts count: 0
200
206
201 parameter with special char in value
207 parameter with special char in value
202 ---------------------------------------------------
208 ---------------------------------------------------
203
209
204 Test generation
210 Test generation
205
211
206 $ hg bundle2 --param 'e|! 7/=babar%#==tutu' --param simple
212 $ hg bundle2 --param 'e|! 7/=babar%#==tutu' --param simple
207 HG20\x00)e%7C%21%207/=babar%25%23%3D%3Dtutu simple\x00\x00 (no-eol) (esc)
213 HG20\x00)e%7C%21%207/=babar%25%23%3D%3Dtutu simple\x00\x00 (no-eol) (esc)
208
214
209 Test unbundling
215 Test unbundling
210
216
211 $ hg bundle2 --param 'e|! 7/=babar%#==tutu' --param simple | hg statbundle2
217 $ hg bundle2 --param 'e|! 7/=babar%#==tutu' --param simple | hg statbundle2
212 options count: 2
218 options count: 2
213 - e|! 7/
219 - e|! 7/
214 babar%#==tutu
220 babar%#==tutu
215 - simple
221 - simple
216 parts count: 0
222 parts count: 0
217
223
218 Test unknown mandatory option
224 Test unknown mandatory option
219 ---------------------------------------------------
225 ---------------------------------------------------
220
226
221 $ hg bundle2 --param 'Gravity' | hg statbundle2
227 $ hg bundle2 --param 'Gravity' | hg statbundle2
222 abort: unknown parameters: 'Gravity'
228 abort: unknown parameters: 'Gravity'
223 [255]
229 [255]
224
230
225 Test debug output
231 Test debug output
226 ---------------------------------------------------
232 ---------------------------------------------------
227
233
228 bundling debug
234 bundling debug
229
235
230 $ hg bundle2 --debug --param 'e|! 7/=babar%#==tutu' --param simple ../out.hg2
236 $ hg bundle2 --debug --param 'e|! 7/=babar%#==tutu' --param simple ../out.hg2
231 start emission of HG20 stream
237 start emission of HG20 stream
232 bundle parameter: e%7C%21%207/=babar%25%23%3D%3Dtutu simple
238 bundle parameter: e%7C%21%207/=babar%25%23%3D%3Dtutu simple
233 start of parts
239 start of parts
234 end of bundle
240 end of bundle
235
241
236 file content is ok
242 file content is ok
237
243
238 $ cat ../out.hg2
244 $ cat ../out.hg2
239 HG20\x00)e%7C%21%207/=babar%25%23%3D%3Dtutu simple\x00\x00 (no-eol) (esc)
245 HG20\x00)e%7C%21%207/=babar%25%23%3D%3Dtutu simple\x00\x00 (no-eol) (esc)
240
246
241 unbundling debug
247 unbundling debug
242
248
243 $ hg statbundle2 --debug < ../out.hg2
249 $ hg statbundle2 --debug < ../out.hg2
244 start processing of HG20 stream
250 start processing of HG20 stream
245 reading bundle2 stream parameters
251 reading bundle2 stream parameters
246 ignoring unknown parameter 'e|! 7/'
252 ignoring unknown parameter 'e|! 7/'
247 ignoring unknown parameter 'simple'
253 ignoring unknown parameter 'simple'
248 options count: 2
254 options count: 2
249 - e|! 7/
255 - e|! 7/
250 babar%#==tutu
256 babar%#==tutu
251 - simple
257 - simple
252 start extraction of bundle2 parts
258 start extraction of bundle2 parts
253 part header size: 0
259 part header size: 0
254 end of bundle2 stream
260 end of bundle2 stream
255 parts count: 0
261 parts count: 0
256
262
257
263
258 Test buggy input
264 Test buggy input
259 ---------------------------------------------------
265 ---------------------------------------------------
260
266
261 empty parameter name
267 empty parameter name
262
268
263 $ hg bundle2 --param '' --quiet
269 $ hg bundle2 --param '' --quiet
264 abort: empty parameter name
270 abort: empty parameter name
265 [255]
271 [255]
266
272
267 bad parameter name
273 bad parameter name
268
274
269 $ hg bundle2 --param 42babar
275 $ hg bundle2 --param 42babar
270 abort: non letter first character: '42babar'
276 abort: non letter first character: '42babar'
271 [255]
277 [255]
272
278
273
279
274 Test part
280 Test part
275 =================
281 =================
276
282
277 $ hg bundle2 --parts ../parts.hg2 --debug
283 $ hg bundle2 --parts ../parts.hg2 --debug
278 start emission of HG20 stream
284 start emission of HG20 stream
279 bundle parameter:
285 bundle parameter:
280 start of parts
286 start of parts
281 bundle part: "test:empty"
287 bundle part: "test:empty"
282 bundle part: "test:empty"
288 bundle part: "test:empty"
283 bundle part: "test:song"
289 bundle part: "test:song"
284 bundle part: "test:math"
290 bundle part: "test:math"
285 end of bundle
291 end of bundle
286
292
287 $ cat ../parts.hg2
293 $ cat ../parts.hg2
288 HG20\x00\x00\x00\r (esc)
294 HG20\x00\x00\x00\r (esc)
289 test:empty\x00\x00\x00\x00\x00\x00\x00\r (esc)
295 test:empty\x00\x00\x00\x00\x00\x00\x00\r (esc)
290 test:empty\x00\x00\x00\x00\x00\x00\x00\x0c test:song\x00\x00\x00\x00\x00\xb2Patali Dirapata, Cromda Cromda Ripalo, Pata Pata, Ko Ko Ko (esc)
296 test:empty\x00\x00\x00\x00\x00\x00\x00\x0c test:song\x00\x00\x00\x00\x00\xb2Patali Dirapata, Cromda Cromda Ripalo, Pata Pata, Ko Ko Ko (esc)
291 Bokoro Dipoulito, Rondi Rondi Pepino, Pata Pata, Ko Ko Ko
297 Bokoro Dipoulito, Rondi Rondi Pepino, Pata Pata, Ko Ko Ko
292 Emana Karassoli, Loucra Loucra Ponponto, Pata Pata, Ko Ko Ko.\x00\x00\x00\x00\x00' test:math\x02\x01\x02\x04\x01\x04\x07\x03pi3.14e2.72cookingraw\x00\x00\x00\x0242\x00\x00\x00\x00\x00\x00 (no-eol) (esc)
298 Emana Karassoli, Loucra Loucra Ponponto, Pata Pata, Ko Ko Ko.\x00\x00\x00\x00\x00' test:math\x02\x01\x02\x04\x01\x04\x07\x03pi3.14e2.72cookingraw\x00\x00\x00\x0242\x00\x00\x00\x00\x00\x00 (no-eol) (esc)
293
299
294
300
295 $ hg statbundle2 < ../parts.hg2
301 $ hg statbundle2 < ../parts.hg2
296 options count: 0
302 options count: 0
297 parts count: 4
303 parts count: 4
298 :test:empty:
304 :test:empty:
299 mandatory: 0
305 mandatory: 0
300 advisory: 0
306 advisory: 0
301 payload: 0 bytes
307 payload: 0 bytes
302 :test:empty:
308 :test:empty:
303 mandatory: 0
309 mandatory: 0
304 advisory: 0
310 advisory: 0
305 payload: 0 bytes
311 payload: 0 bytes
306 :test:song:
312 :test:song:
307 mandatory: 0
313 mandatory: 0
308 advisory: 0
314 advisory: 0
309 payload: 178 bytes
315 payload: 178 bytes
310 :test:math:
316 :test:math:
311 mandatory: 2
317 mandatory: 2
312 advisory: 1
318 advisory: 1
313 payload: 2 bytes
319 payload: 2 bytes
314
320
315 $ hg statbundle2 --debug < ../parts.hg2
321 $ hg statbundle2 --debug < ../parts.hg2
316 start processing of HG20 stream
322 start processing of HG20 stream
317 reading bundle2 stream parameters
323 reading bundle2 stream parameters
318 options count: 0
324 options count: 0
319 start extraction of bundle2 parts
325 start extraction of bundle2 parts
320 part header size: 13
326 part header size: 13
321 part type: "test:empty"
327 part type: "test:empty"
322 part parameters: 0
328 part parameters: 0
323 payload chunk size: 0
329 payload chunk size: 0
324 part header size: 13
330 part header size: 13
325 part type: "test:empty"
331 part type: "test:empty"
326 part parameters: 0
332 part parameters: 0
327 payload chunk size: 0
333 payload chunk size: 0
328 part header size: 12
334 part header size: 12
329 part type: "test:song"
335 part type: "test:song"
330 part parameters: 0
336 part parameters: 0
331 payload chunk size: 178
337 payload chunk size: 178
332 payload chunk size: 0
338 payload chunk size: 0
333 part header size: 39
339 part header size: 39
334 part type: "test:math"
340 part type: "test:math"
335 part parameters: 3
341 part parameters: 3
336 payload chunk size: 2
342 payload chunk size: 2
337 payload chunk size: 0
343 payload chunk size: 0
338 part header size: 0
344 part header size: 0
339 end of bundle2 stream
345 end of bundle2 stream
340 parts count: 4
346 parts count: 4
341 :test:empty:
347 :test:empty:
342 mandatory: 0
348 mandatory: 0
343 advisory: 0
349 advisory: 0
344 payload: 0 bytes
350 payload: 0 bytes
345 :test:empty:
351 :test:empty:
346 mandatory: 0
352 mandatory: 0
347 advisory: 0
353 advisory: 0
348 payload: 0 bytes
354 payload: 0 bytes
349 :test:song:
355 :test:song:
350 mandatory: 0
356 mandatory: 0
351 advisory: 0
357 advisory: 0
352 payload: 178 bytes
358 payload: 178 bytes
353 :test:math:
359 :test:math:
354 mandatory: 2
360 mandatory: 2
355 advisory: 1
361 advisory: 1
356 payload: 2 bytes
362 payload: 2 bytes
357
363
358 Test actual unbundling
364 Test actual unbundling
359 ========================
365 ========================
360
366
361 Process the bundle
367 Process the bundle
362
368
363 $ hg unbundle2 --debug < ../parts.hg2
369 $ hg unbundle2 --debug < ../parts.hg2
364 start processing of HG20 stream
370 start processing of HG20 stream
365 reading bundle2 stream parameters
371 reading bundle2 stream parameters
366 start extraction of bundle2 parts
372 start extraction of bundle2 parts
367 part header size: 13
373 part header size: 13
368 part type: "test:empty"
374 part type: "test:empty"
369 part parameters: 0
375 part parameters: 0
370 payload chunk size: 0
376 payload chunk size: 0
371 ignoring unknown advisory part 'test:empty'
377 ignoring unknown advisory part 'test:empty'
372 part header size: 13
378 part header size: 13
373 part type: "test:empty"
379 part type: "test:empty"
374 part parameters: 0
380 part parameters: 0
375 payload chunk size: 0
381 payload chunk size: 0
376 ignoring unknown advisory part 'test:empty'
382 ignoring unknown advisory part 'test:empty'
377 part header size: 12
383 part header size: 12
378 part type: "test:song"
384 part type: "test:song"
379 part parameters: 0
385 part parameters: 0
380 payload chunk size: 178
386 payload chunk size: 178
381 payload chunk size: 0
387 payload chunk size: 0
382 found an handler for part 'test:song'
388 found an handler for part 'test:song'
383 The choir start singing:
389 The choir start singing:
384 Patali Dirapata, Cromda Cromda Ripalo, Pata Pata, Ko Ko Ko
390 Patali Dirapata, Cromda Cromda Ripalo, Pata Pata, Ko Ko Ko
385 Bokoro Dipoulito, Rondi Rondi Pepino, Pata Pata, Ko Ko Ko
391 Bokoro Dipoulito, Rondi Rondi Pepino, Pata Pata, Ko Ko Ko
386 Emana Karassoli, Loucra Loucra Ponponto, Pata Pata, Ko Ko Ko.
392 Emana Karassoli, Loucra Loucra Ponponto, Pata Pata, Ko Ko Ko.
387 part header size: 39
393 part header size: 39
388 part type: "test:math"
394 part type: "test:math"
389 part parameters: 3
395 part parameters: 3
390 payload chunk size: 2
396 payload chunk size: 2
391 payload chunk size: 0
397 payload chunk size: 0
392 ignoring unknown advisory part 'test:math'
398 ignoring unknown advisory part 'test:math'
393 part header size: 0
399 part header size: 0
394 end of bundle2 stream
400 end of bundle2 stream
395 0 unread bytes
401 0 unread bytes
402 3 total verses sung
396
403
397
404
398 $ hg bundle2 --parts --unknown ../unknown.hg2
405 $ hg bundle2 --parts --unknown ../unknown.hg2
399
406
400 $ hg unbundle2 < ../unknown.hg2
407 $ hg unbundle2 < ../unknown.hg2
401 The choir start singing:
408 The choir start singing:
402 Patali Dirapata, Cromda Cromda Ripalo, Pata Pata, Ko Ko Ko
409 Patali Dirapata, Cromda Cromda Ripalo, Pata Pata, Ko Ko Ko
403 Bokoro Dipoulito, Rondi Rondi Pepino, Pata Pata, Ko Ko Ko
410 Bokoro Dipoulito, Rondi Rondi Pepino, Pata Pata, Ko Ko Ko
404 Emana Karassoli, Loucra Loucra Ponponto, Pata Pata, Ko Ko Ko.
411 Emana Karassoli, Loucra Loucra Ponponto, Pata Pata, Ko Ko Ko.
405 0 unread bytes
412 0 unread bytes
406 abort: missing support for 'test:unknown'
413 abort: missing support for 'test:unknown'
407 [255]
414 [255]
General Comments 0
You need to be logged in to leave comments. Login now