##// END OF EJS Templates
bundle2: lazy unbundle of part payload...
Pierre-Yves David -
r21019:3dc09f83 default
parent child Browse files
Show More
@@ -1,640 +1,662 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 :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 arbitraty content, the binary structure is::
95 Part's parameter may have arbitraty 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 :payload:
116 :payload:
117
117
118 payload is a series of `<chunksize><chunkdata>`.
118 payload is a series of `<chunksize><chunkdata>`.
119
119
120 `chunksize` is a 32 bits integer, `chunkdata` are plain bytes (as much as
120 `chunksize` is a 32 bits integer, `chunkdata` are plain bytes (as much as
121 `chunksize` says)` The payload part is concluded by a zero size chunk.
121 `chunksize` says)` The payload part is concluded by a zero size chunk.
122
122
123 The current implementation always produces either zero or one chunk.
123 The current implementation always produces either zero or one chunk.
124 This is an implementation limitation that will ultimatly be lifted.
124 This is an implementation limitation that will ultimatly be lifted.
125
125
126 Bundle processing
126 Bundle processing
127 ============================
127 ============================
128
128
129 Each part is processed in order using a "part handler". Handler are registered
129 Each part is processed in order using a "part handler". Handler are registered
130 for a certain part type.
130 for a certain part type.
131
131
132 The matching of a part to its handler is case insensitive. The case of the
132 The matching of a part to its handler is case insensitive. The case of the
133 part type is used to know if a part is mandatory or advisory. If the Part type
133 part type is used to know if a part is mandatory or advisory. If the Part type
134 contains any uppercase char it is considered mandatory. When no handler is
134 contains any uppercase char it is considered mandatory. When no handler is
135 known for a Mandatory part, the process is aborted and an exception is raised.
135 known for a Mandatory part, the process is aborted and an exception is raised.
136 If the part is advisory and no handler is known, the part is ignored. When the
136 If the part is advisory and no handler is known, the part is ignored. When the
137 process is aborted, the full bundle is still read from the stream to keep the
137 process is aborted, the full bundle is still read from the stream to keep the
138 channel usable. But none of the part read from an abort are processed. In the
138 channel usable. But none of the part read from an abort are processed. In the
139 future, dropping the stream may become an option for channel we do not care to
139 future, dropping the stream may become an option for channel we do not care to
140 preserve.
140 preserve.
141 """
141 """
142
142
143 import util
143 import util
144 import struct
144 import struct
145 import urllib
145 import urllib
146 import string
146 import string
147 import StringIO
147 import StringIO
148
148
149 import changegroup
149 import changegroup
150 from i18n import _
150 from i18n import _
151
151
152 _pack = struct.pack
152 _pack = struct.pack
153 _unpack = struct.unpack
153 _unpack = struct.unpack
154
154
155 _magicstring = 'HG20'
155 _magicstring = 'HG20'
156
156
157 _fstreamparamsize = '>H'
157 _fstreamparamsize = '>H'
158 _fpartheadersize = '>H'
158 _fpartheadersize = '>H'
159 _fparttypesize = '>B'
159 _fparttypesize = '>B'
160 _fpartid = '>I'
160 _fpartid = '>I'
161 _fpayloadsize = '>I'
161 _fpayloadsize = '>I'
162 _fpartparamcount = '>BB'
162 _fpartparamcount = '>BB'
163
163
164 preferedchunksize = 4096
164 preferedchunksize = 4096
165
165
166 def _makefpartparamsizes(nbparams):
166 def _makefpartparamsizes(nbparams):
167 """return a struct format to read part parameter sizes
167 """return a struct format to read part parameter sizes
168
168
169 The number parameters is variable so we need to build that format
169 The number parameters is variable so we need to build that format
170 dynamically.
170 dynamically.
171 """
171 """
172 return '>'+('BB'*nbparams)
172 return '>'+('BB'*nbparams)
173
173
174 parthandlermapping = {}
174 parthandlermapping = {}
175
175
176 def parthandler(parttype):
176 def parthandler(parttype):
177 """decorator that register a function as a bundle2 part handler
177 """decorator that register a function as a bundle2 part handler
178
178
179 eg::
179 eg::
180
180
181 @parthandler('myparttype')
181 @parthandler('myparttype')
182 def myparttypehandler(...):
182 def myparttypehandler(...):
183 '''process a part of type "my part".'''
183 '''process a part of type "my part".'''
184 ...
184 ...
185 """
185 """
186 def _decorator(func):
186 def _decorator(func):
187 lparttype = parttype.lower() # enforce lower case matching.
187 lparttype = parttype.lower() # enforce lower case matching.
188 assert lparttype not in parthandlermapping
188 assert lparttype not in parthandlermapping
189 parthandlermapping[lparttype] = func
189 parthandlermapping[lparttype] = func
190 return func
190 return func
191 return _decorator
191 return _decorator
192
192
193 class unbundlerecords(object):
193 class unbundlerecords(object):
194 """keep record of what happens during and unbundle
194 """keep record of what happens during and unbundle
195
195
196 New records are added using `records.add('cat', obj)`. Where 'cat' is a
196 New records are added using `records.add('cat', obj)`. Where 'cat' is a
197 category of record and obj is an arbitraty object.
197 category of record and obj is an arbitraty object.
198
198
199 `records['cat']` will return all entries of this category 'cat'.
199 `records['cat']` will return all entries of this category 'cat'.
200
200
201 Iterating on the object itself will yield `('category', obj)` tuples
201 Iterating on the object itself will yield `('category', obj)` tuples
202 for all entries.
202 for all entries.
203
203
204 All iterations happens in chronological order.
204 All iterations happens in chronological order.
205 """
205 """
206
206
207 def __init__(self):
207 def __init__(self):
208 self._categories = {}
208 self._categories = {}
209 self._sequences = []
209 self._sequences = []
210 self._replies = {}
210 self._replies = {}
211
211
212 def add(self, category, entry, inreplyto=None):
212 def add(self, category, entry, inreplyto=None):
213 """add a new record of a given category.
213 """add a new record of a given category.
214
214
215 The entry can then be retrieved in the list returned by
215 The entry can then be retrieved in the list returned by
216 self['category']."""
216 self['category']."""
217 self._categories.setdefault(category, []).append(entry)
217 self._categories.setdefault(category, []).append(entry)
218 self._sequences.append((category, entry))
218 self._sequences.append((category, entry))
219 if inreplyto is not None:
219 if inreplyto is not None:
220 self.getreplies(inreplyto).add(category, entry)
220 self.getreplies(inreplyto).add(category, entry)
221
221
222 def getreplies(self, partid):
222 def getreplies(self, partid):
223 """get the subrecords that replies to a specific part"""
223 """get the subrecords that replies to a specific part"""
224 return self._replies.setdefault(partid, unbundlerecords())
224 return self._replies.setdefault(partid, unbundlerecords())
225
225
226 def __getitem__(self, cat):
226 def __getitem__(self, cat):
227 return tuple(self._categories.get(cat, ()))
227 return tuple(self._categories.get(cat, ()))
228
228
229 def __iter__(self):
229 def __iter__(self):
230 return iter(self._sequences)
230 return iter(self._sequences)
231
231
232 def __len__(self):
232 def __len__(self):
233 return len(self._sequences)
233 return len(self._sequences)
234
234
235 def __nonzero__(self):
235 def __nonzero__(self):
236 return bool(self._sequences)
236 return bool(self._sequences)
237
237
238 class bundleoperation(object):
238 class bundleoperation(object):
239 """an object that represents a single bundling process
239 """an object that represents a single bundling process
240
240
241 Its purpose is to carry unbundle-related objects and states.
241 Its purpose is to carry unbundle-related objects and states.
242
242
243 A new object should be created at the beginning of each bundle processing.
243 A new object should be created at the beginning of each bundle processing.
244 The object is to be returned by the processing function.
244 The object is to be returned by the processing function.
245
245
246 The object has very little content now it will ultimately contain:
246 The object has very little content now it will ultimately contain:
247 * an access to the repo the bundle is applied to,
247 * an access to the repo the bundle is applied to,
248 * a ui object,
248 * a ui object,
249 * a way to retrieve a transaction to add changes to the repo,
249 * a way to retrieve a transaction to add changes to the repo,
250 * a way to record the result of processing each part,
250 * a way to record the result of processing each part,
251 * a way to construct a bundle response when applicable.
251 * a way to construct a bundle response when applicable.
252 """
252 """
253
253
254 def __init__(self, repo, transactiongetter):
254 def __init__(self, repo, transactiongetter):
255 self.repo = repo
255 self.repo = repo
256 self.ui = repo.ui
256 self.ui = repo.ui
257 self.records = unbundlerecords()
257 self.records = unbundlerecords()
258 self.gettransaction = transactiongetter
258 self.gettransaction = transactiongetter
259 self.reply = None
259 self.reply = None
260
260
261 class TransactionUnavailable(RuntimeError):
261 class TransactionUnavailable(RuntimeError):
262 pass
262 pass
263
263
264 def _notransaction():
264 def _notransaction():
265 """default method to get a transaction while processing a bundle
265 """default method to get a transaction while processing a bundle
266
266
267 Raise an exception to highlight the fact that no transaction was expected
267 Raise an exception to highlight the fact that no transaction was expected
268 to be created"""
268 to be created"""
269 raise TransactionUnavailable()
269 raise TransactionUnavailable()
270
270
271 def processbundle(repo, unbundler, transactiongetter=_notransaction):
271 def processbundle(repo, unbundler, transactiongetter=_notransaction):
272 """This function process a bundle, apply effect to/from a repo
272 """This function process a bundle, apply effect to/from a repo
273
273
274 It iterates over each part then searches for and uses the proper handling
274 It iterates over each part then searches for and uses the proper handling
275 code to process the part. Parts are processed in order.
275 code to process the part. Parts are processed in order.
276
276
277 This is very early version of this function that will be strongly reworked
277 This is very early version of this function that will be strongly reworked
278 before final usage.
278 before final usage.
279
279
280 Unknown Mandatory part will abort the process.
280 Unknown Mandatory part will abort the process.
281 """
281 """
282 op = bundleoperation(repo, transactiongetter)
282 op = bundleoperation(repo, transactiongetter)
283 # todo:
283 # todo:
284 # - only create reply bundle if requested.
284 # - only create reply bundle if requested.
285 op.reply = bundle20(op.ui)
285 op.reply = bundle20(op.ui)
286 # todo:
286 # todo:
287 # - replace this is a init function soon.
287 # - replace this is a init function soon.
288 # - exception catching
288 # - exception catching
289 unbundler.params
289 unbundler.params
290 iterparts = iter(unbundler)
290 iterparts = iter(unbundler)
291 part = None
291 try:
292 try:
292 for part in iterparts:
293 for part in iterparts:
293 parttype = part.type
294 parttype = part.type
294 # part key are matched lower case
295 # part key are matched lower case
295 key = parttype.lower()
296 key = parttype.lower()
296 try:
297 try:
297 handler = parthandlermapping[key]
298 handler = parthandlermapping[key]
298 op.ui.debug('found a handler for part %r\n' % parttype)
299 op.ui.debug('found a handler for part %r\n' % parttype)
299 except KeyError:
300 except KeyError:
300 if key != parttype: # mandatory parts
301 if key != parttype: # mandatory parts
301 # todo:
302 # todo:
302 # - use a more precise exception
303 # - use a more precise exception
303 raise
304 raise
304 op.ui.debug('ignoring unknown advisory part %r\n' % key)
305 op.ui.debug('ignoring unknown advisory part %r\n' % key)
305 # todo:
306 # consuming the part
306 # - consume the part once we use streaming
307 part.read()
307 continue
308 continue
308
309
309 # handler is called outside the above try block so that we don't
310 # handler is called outside the above try block so that we don't
310 # risk catching KeyErrors from anything other than the
311 # risk catching KeyErrors from anything other than the
311 # parthandlermapping lookup (any KeyError raised by handler()
312 # parthandlermapping lookup (any KeyError raised by handler()
312 # itself represents a defect of a different variety).
313 # itself represents a defect of a different variety).
313 handler(op, part)
314 handler(op, part)
315 part.read()
314 except Exception:
316 except Exception:
317 if part is not None:
318 # consume the bundle content
319 part.read()
315 for part in iterparts:
320 for part in iterparts:
316 pass # consume the bundle content
321 # consume the bundle content
322 part.read()
317 raise
323 raise
318 return op
324 return op
319
325
320 class bundle20(object):
326 class bundle20(object):
321 """represent an outgoing bundle2 container
327 """represent an outgoing bundle2 container
322
328
323 Use the `addparam` method to add stream level parameter. and `addpart` to
329 Use the `addparam` method to add stream level parameter. and `addpart` to
324 populate it. Then call `getchunks` to retrieve all the binary chunks of
330 populate it. Then call `getchunks` to retrieve all the binary chunks of
325 datathat compose the bundle2 container."""
331 datathat compose the bundle2 container."""
326
332
327 def __init__(self, ui):
333 def __init__(self, ui):
328 self.ui = ui
334 self.ui = ui
329 self._params = []
335 self._params = []
330 self._parts = []
336 self._parts = []
331
337
332 def addparam(self, name, value=None):
338 def addparam(self, name, value=None):
333 """add a stream level parameter"""
339 """add a stream level parameter"""
334 if not name:
340 if not name:
335 raise ValueError('empty parameter name')
341 raise ValueError('empty parameter name')
336 if name[0] not in string.letters:
342 if name[0] not in string.letters:
337 raise ValueError('non letter first character: %r' % name)
343 raise ValueError('non letter first character: %r' % name)
338 self._params.append((name, value))
344 self._params.append((name, value))
339
345
340 def addpart(self, part):
346 def addpart(self, part):
341 """add a new part to the bundle2 container
347 """add a new part to the bundle2 container
342
348
343 Parts contains the actuall applicative payload."""
349 Parts contains the actuall applicative payload."""
344 assert part.id is None
350 assert part.id is None
345 part.id = len(self._parts) # very cheap counter
351 part.id = len(self._parts) # very cheap counter
346 self._parts.append(part)
352 self._parts.append(part)
347
353
348 def getchunks(self):
354 def getchunks(self):
349 self.ui.debug('start emission of %s stream\n' % _magicstring)
355 self.ui.debug('start emission of %s stream\n' % _magicstring)
350 yield _magicstring
356 yield _magicstring
351 param = self._paramchunk()
357 param = self._paramchunk()
352 self.ui.debug('bundle parameter: %s\n' % param)
358 self.ui.debug('bundle parameter: %s\n' % param)
353 yield _pack(_fstreamparamsize, len(param))
359 yield _pack(_fstreamparamsize, len(param))
354 if param:
360 if param:
355 yield param
361 yield param
356
362
357 self.ui.debug('start of parts\n')
363 self.ui.debug('start of parts\n')
358 for part in self._parts:
364 for part in self._parts:
359 self.ui.debug('bundle part: "%s"\n' % part.type)
365 self.ui.debug('bundle part: "%s"\n' % part.type)
360 for chunk in part.getchunks():
366 for chunk in part.getchunks():
361 yield chunk
367 yield chunk
362 self.ui.debug('end of bundle\n')
368 self.ui.debug('end of bundle\n')
363 yield '\0\0'
369 yield '\0\0'
364
370
365 def _paramchunk(self):
371 def _paramchunk(self):
366 """return a encoded version of all stream parameters"""
372 """return a encoded version of all stream parameters"""
367 blocks = []
373 blocks = []
368 for par, value in self._params:
374 for par, value in self._params:
369 par = urllib.quote(par)
375 par = urllib.quote(par)
370 if value is not None:
376 if value is not None:
371 value = urllib.quote(value)
377 value = urllib.quote(value)
372 par = '%s=%s' % (par, value)
378 par = '%s=%s' % (par, value)
373 blocks.append(par)
379 blocks.append(par)
374 return ' '.join(blocks)
380 return ' '.join(blocks)
375
381
376 class unpackermixin(object):
382 class unpackermixin(object):
377 """A mixin to extract bytes and struct data from a stream"""
383 """A mixin to extract bytes and struct data from a stream"""
378
384
379 def __init__(self, fp):
385 def __init__(self, fp):
380 self._fp = fp
386 self._fp = fp
381
387
382 def _unpack(self, format):
388 def _unpack(self, format):
383 """unpack this struct format from the stream"""
389 """unpack this struct format from the stream"""
384 data = self._readexact(struct.calcsize(format))
390 data = self._readexact(struct.calcsize(format))
385 return _unpack(format, data)
391 return _unpack(format, data)
386
392
387 def _readexact(self, size):
393 def _readexact(self, size):
388 """read exactly <size> bytes from the stream"""
394 """read exactly <size> bytes from the stream"""
389 return changegroup.readexactly(self._fp, size)
395 return changegroup.readexactly(self._fp, size)
390
396
391
397
392 class unbundle20(unpackermixin):
398 class unbundle20(unpackermixin):
393 """interpret a bundle2 stream
399 """interpret a bundle2 stream
394
400
395 (this will eventually yield parts)"""
401 (this will eventually yield parts)"""
396
402
397 def __init__(self, ui, fp):
403 def __init__(self, ui, fp):
398 self.ui = ui
404 self.ui = ui
399 super(unbundle20, self).__init__(fp)
405 super(unbundle20, self).__init__(fp)
400 header = self._readexact(4)
406 header = self._readexact(4)
401 magic, version = header[0:2], header[2:4]
407 magic, version = header[0:2], header[2:4]
402 if magic != 'HG':
408 if magic != 'HG':
403 raise util.Abort(_('not a Mercurial bundle'))
409 raise util.Abort(_('not a Mercurial bundle'))
404 if version != '20':
410 if version != '20':
405 raise util.Abort(_('unknown bundle version %s') % version)
411 raise util.Abort(_('unknown bundle version %s') % version)
406 self.ui.debug('start processing of %s stream\n' % header)
412 self.ui.debug('start processing of %s stream\n' % header)
407
413
408 @util.propertycache
414 @util.propertycache
409 def params(self):
415 def params(self):
410 """dictionnary of stream level parameters"""
416 """dictionnary of stream level parameters"""
411 self.ui.debug('reading bundle2 stream parameters\n')
417 self.ui.debug('reading bundle2 stream parameters\n')
412 params = {}
418 params = {}
413 paramssize = self._unpack(_fstreamparamsize)[0]
419 paramssize = self._unpack(_fstreamparamsize)[0]
414 if paramssize:
420 if paramssize:
415 for p in self._readexact(paramssize).split(' '):
421 for p in self._readexact(paramssize).split(' '):
416 p = p.split('=', 1)
422 p = p.split('=', 1)
417 p = [urllib.unquote(i) for i in p]
423 p = [urllib.unquote(i) for i in p]
418 if len(p) < 2:
424 if len(p) < 2:
419 p.append(None)
425 p.append(None)
420 self._processparam(*p)
426 self._processparam(*p)
421 params[p[0]] = p[1]
427 params[p[0]] = p[1]
422 return params
428 return params
423
429
424 def _processparam(self, name, value):
430 def _processparam(self, name, value):
425 """process a parameter, applying its effect if needed
431 """process a parameter, applying its effect if needed
426
432
427 Parameter starting with a lower case letter are advisory and will be
433 Parameter starting with a lower case letter are advisory and will be
428 ignored when unknown. Those starting with an upper case letter are
434 ignored when unknown. Those starting with an upper case letter are
429 mandatory and will this function will raise a KeyError when unknown.
435 mandatory and will this function will raise a KeyError when unknown.
430
436
431 Note: no option are currently supported. Any input will be either
437 Note: no option are currently supported. Any input will be either
432 ignored or failing.
438 ignored or failing.
433 """
439 """
434 if not name:
440 if not name:
435 raise ValueError('empty parameter name')
441 raise ValueError('empty parameter name')
436 if name[0] not in string.letters:
442 if name[0] not in string.letters:
437 raise ValueError('non letter first character: %r' % name)
443 raise ValueError('non letter first character: %r' % name)
438 # Some logic will be later added here to try to process the option for
444 # Some logic will be later added here to try to process the option for
439 # a dict of known parameter.
445 # a dict of known parameter.
440 if name[0].islower():
446 if name[0].islower():
441 self.ui.debug("ignoring unknown parameter %r\n" % name)
447 self.ui.debug("ignoring unknown parameter %r\n" % name)
442 else:
448 else:
443 raise KeyError(name)
449 raise KeyError(name)
444
450
445
451
446 def __iter__(self):
452 def __iter__(self):
447 """yield all parts contained in the stream"""
453 """yield all parts contained in the stream"""
448 # make sure param have been loaded
454 # make sure param have been loaded
449 self.params
455 self.params
450 self.ui.debug('start extraction of bundle2 parts\n')
456 self.ui.debug('start extraction of bundle2 parts\n')
451 headerblock = self._readpartheader()
457 headerblock = self._readpartheader()
452 while headerblock is not None:
458 while headerblock is not None:
453 part = unbundlepart(self.ui, headerblock, self._fp)
459 part = unbundlepart(self.ui, headerblock, self._fp)
454 yield part
460 yield part
455 headerblock = self._readpartheader()
461 headerblock = self._readpartheader()
456 self.ui.debug('end of bundle2 stream\n')
462 self.ui.debug('end of bundle2 stream\n')
457
463
458 def _readpartheader(self):
464 def _readpartheader(self):
459 """reads a part header size and return the bytes blob
465 """reads a part header size and return the bytes blob
460
466
461 returns None if empty"""
467 returns None if empty"""
462 headersize = self._unpack(_fpartheadersize)[0]
468 headersize = self._unpack(_fpartheadersize)[0]
463 self.ui.debug('part header size: %i\n' % headersize)
469 self.ui.debug('part header size: %i\n' % headersize)
464 if headersize:
470 if headersize:
465 return self._readexact(headersize)
471 return self._readexact(headersize)
466 return None
472 return None
467
473
468
474
469 class bundlepart(object):
475 class bundlepart(object):
470 """A bundle2 part contains application level payload
476 """A bundle2 part contains application level payload
471
477
472 The part `type` is used to route the part to the application level
478 The part `type` is used to route the part to the application level
473 handler.
479 handler.
474 """
480 """
475
481
476 def __init__(self, parttype, mandatoryparams=(), advisoryparams=(),
482 def __init__(self, parttype, mandatoryparams=(), advisoryparams=(),
477 data=''):
483 data=''):
478 self.id = None
484 self.id = None
479 self.type = parttype
485 self.type = parttype
480 self.data = data
486 self.data = data
481 self.mandatoryparams = mandatoryparams
487 self.mandatoryparams = mandatoryparams
482 self.advisoryparams = advisoryparams
488 self.advisoryparams = advisoryparams
483
489
484 def getchunks(self):
490 def getchunks(self):
485 #### header
491 #### header
486 ## parttype
492 ## parttype
487 header = [_pack(_fparttypesize, len(self.type)),
493 header = [_pack(_fparttypesize, len(self.type)),
488 self.type, _pack(_fpartid, self.id),
494 self.type, _pack(_fpartid, self.id),
489 ]
495 ]
490 ## parameters
496 ## parameters
491 # count
497 # count
492 manpar = self.mandatoryparams
498 manpar = self.mandatoryparams
493 advpar = self.advisoryparams
499 advpar = self.advisoryparams
494 header.append(_pack(_fpartparamcount, len(manpar), len(advpar)))
500 header.append(_pack(_fpartparamcount, len(manpar), len(advpar)))
495 # size
501 # size
496 parsizes = []
502 parsizes = []
497 for key, value in manpar:
503 for key, value in manpar:
498 parsizes.append(len(key))
504 parsizes.append(len(key))
499 parsizes.append(len(value))
505 parsizes.append(len(value))
500 for key, value in advpar:
506 for key, value in advpar:
501 parsizes.append(len(key))
507 parsizes.append(len(key))
502 parsizes.append(len(value))
508 parsizes.append(len(value))
503 paramsizes = _pack(_makefpartparamsizes(len(parsizes) / 2), *parsizes)
509 paramsizes = _pack(_makefpartparamsizes(len(parsizes) / 2), *parsizes)
504 header.append(paramsizes)
510 header.append(paramsizes)
505 # key, value
511 # key, value
506 for key, value in manpar:
512 for key, value in manpar:
507 header.append(key)
513 header.append(key)
508 header.append(value)
514 header.append(value)
509 for key, value in advpar:
515 for key, value in advpar:
510 header.append(key)
516 header.append(key)
511 header.append(value)
517 header.append(value)
512 ## finalize header
518 ## finalize header
513 headerchunk = ''.join(header)
519 headerchunk = ''.join(header)
514 yield _pack(_fpartheadersize, len(headerchunk))
520 yield _pack(_fpartheadersize, len(headerchunk))
515 yield headerchunk
521 yield headerchunk
516 ## payload
522 ## payload
517 for chunk in self._payloadchunks():
523 for chunk in self._payloadchunks():
518 yield _pack(_fpayloadsize, len(chunk))
524 yield _pack(_fpayloadsize, len(chunk))
519 yield chunk
525 yield chunk
520 # end of payload
526 # end of payload
521 yield _pack(_fpayloadsize, 0)
527 yield _pack(_fpayloadsize, 0)
522
528
523 def _payloadchunks(self):
529 def _payloadchunks(self):
524 """yield chunks of a the part payload
530 """yield chunks of a the part payload
525
531
526 Exists to handle the different methods to provide data to a part."""
532 Exists to handle the different methods to provide data to a part."""
527 # we only support fixed size data now.
533 # we only support fixed size data now.
528 # This will be improved in the future.
534 # This will be improved in the future.
529 if util.safehasattr(self.data, 'next'):
535 if util.safehasattr(self.data, 'next'):
530 buff = util.chunkbuffer(self.data)
536 buff = util.chunkbuffer(self.data)
531 chunk = buff.read(preferedchunksize)
537 chunk = buff.read(preferedchunksize)
532 while chunk:
538 while chunk:
533 yield chunk
539 yield chunk
534 chunk = buff.read(preferedchunksize)
540 chunk = buff.read(preferedchunksize)
535 elif len(self.data):
541 elif len(self.data):
536 yield self.data
542 yield self.data
537
543
538 class unbundlepart(unpackermixin):
544 class unbundlepart(unpackermixin):
539 """a bundle part read from a bundle"""
545 """a bundle part read from a bundle"""
540
546
541 def __init__(self, ui, header, fp):
547 def __init__(self, ui, header, fp):
542 super(unbundlepart, self).__init__(fp)
548 super(unbundlepart, self).__init__(fp)
543 self.ui = ui
549 self.ui = ui
544 # unbundle state attr
550 # unbundle state attr
545 self._headerdata = header
551 self._headerdata = header
546 self._headeroffset = 0
552 self._headeroffset = 0
553 self._initialized = False
554 self.consumed = False
547 # part data
555 # part data
548 self.id = None
556 self.id = None
549 self.type = None
557 self.type = None
550 self.mandatoryparams = None
558 self.mandatoryparams = None
551 self.advisoryparams = None
559 self.advisoryparams = None
552 self.data = None
560 self._payloadstream = None
553 self._readdata()
561 self._readheader()
554
562
555 def _fromheader(self, size):
563 def _fromheader(self, size):
556 """return the next <size> byte from the header"""
564 """return the next <size> byte from the header"""
557 offset = self._headeroffset
565 offset = self._headeroffset
558 data = self._headerdata[offset:(offset + size)]
566 data = self._headerdata[offset:(offset + size)]
559 self._headeroffset += size
567 self._headeroffset = offset + size
560 return data
568 return data
561
569
562 def _unpackheader(self, format):
570 def _unpackheader(self, format):
563 """read given format from header
571 """read given format from header
564
572
565 This automatically compute the size of the format to read."""
573 This automatically compute the size of the format to read."""
566 data = self._fromheader(struct.calcsize(format))
574 data = self._fromheader(struct.calcsize(format))
567 return _unpack(format, data)
575 return _unpack(format, data)
568
576
569 def _readdata(self):
577 def _readheader(self):
570 """read the header and setup the object"""
578 """read the header and setup the object"""
571 # some utility to help reading from the header block
572
573 typesize = self._unpackheader(_fparttypesize)[0]
579 typesize = self._unpackheader(_fparttypesize)[0]
574 self.type = self._fromheader(typesize)
580 self.type = self._fromheader(typesize)
575 self.ui.debug('part type: "%s"\n' % self.type)
581 self.ui.debug('part type: "%s"\n' % self.type)
576 self.id = self._unpackheader(_fpartid)[0]
582 self.id = self._unpackheader(_fpartid)[0]
577 self.ui.debug('part id: "%s"\n' % self.id)
583 self.ui.debug('part id: "%s"\n' % self.id)
578 ## reading parameters
584 ## reading parameters
579 # param count
585 # param count
580 mancount, advcount = self._unpackheader(_fpartparamcount)
586 mancount, advcount = self._unpackheader(_fpartparamcount)
581 self.ui.debug('part parameters: %i\n' % (mancount + advcount))
587 self.ui.debug('part parameters: %i\n' % (mancount + advcount))
582 # param size
588 # param size
583 fparamsizes = _makefpartparamsizes(mancount + advcount)
589 fparamsizes = _makefpartparamsizes(mancount + advcount)
584 paramsizes = self._unpackheader(fparamsizes)
590 paramsizes = self._unpackheader(fparamsizes)
585 # make it a list of couple again
591 # make it a list of couple again
586 paramsizes = zip(paramsizes[::2], paramsizes[1::2])
592 paramsizes = zip(paramsizes[::2], paramsizes[1::2])
587 # split mandatory from advisory
593 # split mandatory from advisory
588 mansizes = paramsizes[:mancount]
594 mansizes = paramsizes[:mancount]
589 advsizes = paramsizes[mancount:]
595 advsizes = paramsizes[mancount:]
590 # retrive param value
596 # retrive param value
591 manparams = []
597 manparams = []
592 for key, value in mansizes:
598 for key, value in mansizes:
593 manparams.append((self._fromheader(key), self._fromheader(value)))
599 manparams.append((self._fromheader(key), self._fromheader(value)))
594 advparams = []
600 advparams = []
595 for key, value in advsizes:
601 for key, value in advsizes:
596 advparams.append((self._fromheader(key), self._fromheader(value)))
602 advparams.append((self._fromheader(key), self._fromheader(value)))
597 self.mandatoryparams = manparams
603 self.mandatoryparams = manparams
598 self.advisoryparams = advparams
604 self.advisoryparams = advparams
599 ## part payload
605 ## part payload
600 payload = []
606 def payloadchunks():
601 payloadsize = self._unpack(_fpayloadsize)[0]
607 payloadsize = self._unpack(_fpayloadsize)[0]
602 self.ui.debug('payload chunk size: %i\n' % payloadsize)
608 self.ui.debug('payload chunk size: %i\n' % payloadsize)
603 while payloadsize:
609 while payloadsize:
604 payload.append(self._readexact(payloadsize))
610 yield self._readexact(payloadsize)
605 payloadsize = self._unpack(_fpayloadsize)[0]
611 payloadsize = self._unpack(_fpayloadsize)[0]
606 self.ui.debug('payload chunk size: %i\n' % payloadsize)
612 self.ui.debug('payload chunk size: %i\n' % payloadsize)
607 self.data = ''.join(payload)
613 self._payloadstream = util.chunkbuffer(payloadchunks())
614 # we read the data, tell it
615 self._initialized = True
616
617 def read(self, size=None):
618 """read payload data"""
619 if not self._initialized:
620 self._readheader()
621 if size is None:
622 data = self._payloadstream.read()
623 else:
624 data = self._payloadstream.read(size)
625 if size is None or len(data) < size:
626 self.consumed = True
627 return data
628
608
629
609 @parthandler('changegroup')
630 @parthandler('changegroup')
610 def handlechangegroup(op, inpart):
631 def handlechangegroup(op, inpart):
611 """apply a changegroup part on the repo
632 """apply a changegroup part on the repo
612
633
613 This is a very early implementation that will massive rework before being
634 This is a very early implementation that will massive rework before being
614 inflicted to any end-user.
635 inflicted to any end-user.
615 """
636 """
616 # Make sure we trigger a transaction creation
637 # Make sure we trigger a transaction creation
617 #
638 #
618 # The addchangegroup function will get a transaction object by itself, but
639 # The addchangegroup function will get a transaction object by itself, but
619 # we need to make sure we trigger the creation of a transaction object used
640 # we need to make sure we trigger the creation of a transaction object used
620 # for the whole processing scope.
641 # for the whole processing scope.
621 op.gettransaction()
642 op.gettransaction()
622 data = StringIO.StringIO(inpart.data)
643 data = StringIO.StringIO(inpart.read())
623 data.seek(0)
644 data.seek(0)
624 cg = changegroup.readbundle(data, 'bundle2part')
645 cg = changegroup.readbundle(data, 'bundle2part')
625 ret = changegroup.addchangegroup(op.repo, cg, 'bundle2', 'bundle2')
646 ret = changegroup.addchangegroup(op.repo, cg, 'bundle2', 'bundle2')
626 op.records.add('changegroup', {'return': ret})
647 op.records.add('changegroup', {'return': ret})
627 if op.reply is not None:
648 if op.reply is not None:
628 # This is definitly not the final form of this
649 # This is definitly not the final form of this
629 # return. But one need to start somewhere.
650 # return. But one need to start somewhere.
630 part = bundlepart('reply:changegroup', (),
651 part = bundlepart('reply:changegroup', (),
631 [('in-reply-to', str(inpart.id)),
652 [('in-reply-to', str(inpart.id)),
632 ('return', '%i' % ret)])
653 ('return', '%i' % ret)])
633 op.reply.addpart(part)
654 op.reply.addpart(part)
655 assert not inpart.read()
634
656
635 @parthandler('reply:changegroup')
657 @parthandler('reply:changegroup')
636 def handlechangegroup(op, inpart):
658 def handlechangegroup(op, inpart):
637 p = dict(inpart.advisoryparams)
659 p = dict(inpart.advisoryparams)
638 ret = int(p['return'])
660 ret = int(p['return'])
639 op.records.add('changegroup', {'return': ret}, int(p['in-reply-to']))
661 op.records.add('changegroup', {'return': ret}, int(p['in-reply-to']))
640
662
@@ -1,678 +1,678 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 > from mercurial import scmutil
15 > from mercurial import scmutil
16 > from mercurial import discovery
16 > from mercurial import discovery
17 > from mercurial import changegroup
17 > from mercurial import changegroup
18 > cmdtable = {}
18 > cmdtable = {}
19 > command = cmdutil.command(cmdtable)
19 > command = cmdutil.command(cmdtable)
20 >
20 >
21 > ELEPHANTSSONG = """Patali Dirapata, Cromda Cromda Ripalo, Pata Pata, Ko Ko Ko
21 > ELEPHANTSSONG = """Patali Dirapata, Cromda Cromda Ripalo, Pata Pata, Ko Ko Ko
22 > Bokoro Dipoulito, Rondi Rondi Pepino, Pata Pata, Ko Ko Ko
22 > Bokoro Dipoulito, Rondi Rondi Pepino, Pata Pata, Ko Ko Ko
23 > Emana Karassoli, Loucra Loucra Ponponto, Pata Pata, Ko Ko Ko."""
23 > Emana Karassoli, Loucra Loucra Ponponto, Pata Pata, Ko Ko Ko."""
24 > assert len(ELEPHANTSSONG) == 178 # future test say 178 bytes, trust it.
24 > assert len(ELEPHANTSSONG) == 178 # future test say 178 bytes, trust it.
25 >
25 >
26 > @bundle2.parthandler('test:song')
26 > @bundle2.parthandler('test:song')
27 > def songhandler(op, part):
27 > def songhandler(op, part):
28 > """handle a "test:song" bundle2 part, printing the lyrics on stdin"""
28 > """handle a "test:song" bundle2 part, printing the lyrics on stdin"""
29 > op.ui.write('The choir starts singing:\n')
29 > op.ui.write('The choir starts singing:\n')
30 > verses = 0
30 > verses = 0
31 > for line in part.data.split('\n'):
31 > for line in part.read().split('\n'):
32 > op.ui.write(' %s\n' % line)
32 > op.ui.write(' %s\n' % line)
33 > verses += 1
33 > verses += 1
34 > op.records.add('song', {'verses': verses})
34 > op.records.add('song', {'verses': verses})
35 >
35 >
36 > @bundle2.parthandler('test:ping')
36 > @bundle2.parthandler('test:ping')
37 > def pinghandler(op, part):
37 > def pinghandler(op, part):
38 > op.ui.write('received ping request (id %i)\n' % part.id)
38 > op.ui.write('received ping request (id %i)\n' % part.id)
39 > if op.reply is not None:
39 > if op.reply is not None:
40 > rpart = bundle2.bundlepart('test:pong',
40 > rpart = bundle2.bundlepart('test:pong',
41 > [('in-reply-to', str(part.id))])
41 > [('in-reply-to', str(part.id))])
42 > op.reply.addpart(rpart)
42 > op.reply.addpart(rpart)
43 >
43 >
44 > @command('bundle2',
44 > @command('bundle2',
45 > [('', 'param', [], 'stream level parameter'),
45 > [('', 'param', [], 'stream level parameter'),
46 > ('', 'unknown', False, 'include an unknown mandatory part in the bundle'),
46 > ('', 'unknown', False, 'include an unknown mandatory part in the bundle'),
47 > ('', 'parts', False, 'include some arbitrary parts to the bundle'),
47 > ('', 'parts', False, 'include some arbitrary parts to the bundle'),
48 > ('r', 'rev', [], 'includes those changeset in the bundle'),],
48 > ('r', 'rev', [], 'includes those changeset in the bundle'),],
49 > '[OUTPUTFILE]')
49 > '[OUTPUTFILE]')
50 > def cmdbundle2(ui, repo, path=None, **opts):
50 > def cmdbundle2(ui, repo, path=None, **opts):
51 > """write a bundle2 container on standard ouput"""
51 > """write a bundle2 container on standard ouput"""
52 > bundler = bundle2.bundle20(ui)
52 > bundler = bundle2.bundle20(ui)
53 > for p in opts['param']:
53 > for p in opts['param']:
54 > p = p.split('=', 1)
54 > p = p.split('=', 1)
55 > try:
55 > try:
56 > bundler.addparam(*p)
56 > bundler.addparam(*p)
57 > except ValueError, exc:
57 > except ValueError, exc:
58 > raise util.Abort('%s' % exc)
58 > raise util.Abort('%s' % exc)
59 >
59 >
60 > revs = opts['rev']
60 > revs = opts['rev']
61 > if 'rev' in opts:
61 > if 'rev' in opts:
62 > revs = scmutil.revrange(repo, opts['rev'])
62 > revs = scmutil.revrange(repo, opts['rev'])
63 > if revs:
63 > if revs:
64 > # very crude version of a changegroup part creation
64 > # very crude version of a changegroup part creation
65 > bundled = repo.revs('%ld::%ld', revs, revs)
65 > bundled = repo.revs('%ld::%ld', revs, revs)
66 > headmissing = [c.node() for c in repo.set('heads(%ld)', revs)]
66 > headmissing = [c.node() for c in repo.set('heads(%ld)', revs)]
67 > headcommon = [c.node() for c in repo.set('parents(%ld) - %ld', revs, revs)]
67 > headcommon = [c.node() for c in repo.set('parents(%ld) - %ld', revs, revs)]
68 > outgoing = discovery.outgoing(repo.changelog, headcommon, headmissing)
68 > outgoing = discovery.outgoing(repo.changelog, headcommon, headmissing)
69 > cg = changegroup.getlocalbundle(repo, 'test:bundle2', outgoing, None)
69 > cg = changegroup.getlocalbundle(repo, 'test:bundle2', outgoing, None)
70 > def cgchunks(cg=cg):
70 > def cgchunks(cg=cg):
71 > yield 'HG10UN'
71 > yield 'HG10UN'
72 > for c in cg.getchunks():
72 > for c in cg.getchunks():
73 > yield c
73 > yield c
74 > part = bundle2.bundlepart('changegroup', data=cgchunks())
74 > part = bundle2.bundlepart('changegroup', data=cgchunks())
75 > bundler.addpart(part)
75 > bundler.addpart(part)
76 >
76 >
77 > if opts['parts']:
77 > if opts['parts']:
78 > part = bundle2.bundlepart('test:empty')
78 > part = bundle2.bundlepart('test:empty')
79 > bundler.addpart(part)
79 > bundler.addpart(part)
80 > # add a second one to make sure we handle multiple parts
80 > # add a second one to make sure we handle multiple parts
81 > part = bundle2.bundlepart('test:empty')
81 > part = bundle2.bundlepart('test:empty')
82 > bundler.addpart(part)
82 > bundler.addpart(part)
83 > part = bundle2.bundlepart('test:song', data=ELEPHANTSSONG)
83 > part = bundle2.bundlepart('test:song', data=ELEPHANTSSONG)
84 > bundler.addpart(part)
84 > bundler.addpart(part)
85 > part = bundle2.bundlepart('test:math',
85 > part = bundle2.bundlepart('test:math',
86 > [('pi', '3.14'), ('e', '2.72')],
86 > [('pi', '3.14'), ('e', '2.72')],
87 > [('cooking', 'raw')],
87 > [('cooking', 'raw')],
88 > '42')
88 > '42')
89 > bundler.addpart(part)
89 > bundler.addpart(part)
90 > if opts['unknown']:
90 > if opts['unknown']:
91 > part = bundle2.bundlepart('test:UNKNOWN',
91 > part = bundle2.bundlepart('test:UNKNOWN',
92 > data='some random content')
92 > data='some random content')
93 > bundler.addpart(part)
93 > bundler.addpart(part)
94 > if opts['parts']:
94 > if opts['parts']:
95 > part = bundle2.bundlepart('test:ping')
95 > part = bundle2.bundlepart('test:ping')
96 > bundler.addpart(part)
96 > bundler.addpart(part)
97 >
97 >
98 > if path is None:
98 > if path is None:
99 > file = sys.stdout
99 > file = sys.stdout
100 > else:
100 > else:
101 > file = open(path, 'w')
101 > file = open(path, 'w')
102 >
102 >
103 > for chunk in bundler.getchunks():
103 > for chunk in bundler.getchunks():
104 > file.write(chunk)
104 > file.write(chunk)
105 >
105 >
106 > @command('unbundle2', [], '')
106 > @command('unbundle2', [], '')
107 > def cmdunbundle2(ui, repo, replypath=None):
107 > def cmdunbundle2(ui, repo, replypath=None):
108 > """process a bundle2 stream from stdin on the current repo"""
108 > """process a bundle2 stream from stdin on the current repo"""
109 > try:
109 > try:
110 > tr = None
110 > tr = None
111 > lock = repo.lock()
111 > lock = repo.lock()
112 > tr = repo.transaction('processbundle')
112 > tr = repo.transaction('processbundle')
113 > try:
113 > try:
114 > unbundler = bundle2.unbundle20(ui, sys.stdin)
114 > unbundler = bundle2.unbundle20(ui, sys.stdin)
115 > op = bundle2.processbundle(repo, unbundler, lambda: tr)
115 > op = bundle2.processbundle(repo, unbundler, lambda: tr)
116 > tr.close()
116 > tr.close()
117 > except KeyError, exc:
117 > except KeyError, exc:
118 > raise util.Abort('missing support for %s' % exc)
118 > raise util.Abort('missing support for %s' % exc)
119 > finally:
119 > finally:
120 > if tr is not None:
120 > if tr is not None:
121 > tr.release()
121 > tr.release()
122 > lock.release()
122 > lock.release()
123 > remains = sys.stdin.read()
123 > remains = sys.stdin.read()
124 > ui.write('%i unread bytes\n' % len(remains))
124 > ui.write('%i unread bytes\n' % len(remains))
125 > if op.records['song']:
125 > if op.records['song']:
126 > totalverses = sum(r['verses'] for r in op.records['song'])
126 > totalverses = sum(r['verses'] for r in op.records['song'])
127 > ui.write('%i total verses sung\n' % totalverses)
127 > ui.write('%i total verses sung\n' % totalverses)
128 > for rec in op.records['changegroup']:
128 > for rec in op.records['changegroup']:
129 > ui.write('addchangegroup return: %i\n' % rec['return'])
129 > ui.write('addchangegroup return: %i\n' % rec['return'])
130 > if op.reply is not None and replypath is not None:
130 > if op.reply is not None and replypath is not None:
131 > file = open(replypath, 'w')
131 > file = open(replypath, 'w')
132 > for chunk in op.reply.getchunks():
132 > for chunk in op.reply.getchunks():
133 > file.write(chunk)
133 > file.write(chunk)
134 >
134 >
135 > @command('statbundle2', [], '')
135 > @command('statbundle2', [], '')
136 > def cmdstatbundle2(ui, repo):
136 > def cmdstatbundle2(ui, repo):
137 > """print statistic on the bundle2 container read from stdin"""
137 > """print statistic on the bundle2 container read from stdin"""
138 > unbundler = bundle2.unbundle20(ui, sys.stdin)
138 > unbundler = bundle2.unbundle20(ui, sys.stdin)
139 > try:
139 > try:
140 > params = unbundler.params
140 > params = unbundler.params
141 > except KeyError, exc:
141 > except KeyError, exc:
142 > raise util.Abort('unknown parameters: %s' % exc)
142 > raise util.Abort('unknown parameters: %s' % exc)
143 > ui.write('options count: %i\n' % len(params))
143 > ui.write('options count: %i\n' % len(params))
144 > for key in sorted(params):
144 > for key in sorted(params):
145 > ui.write('- %s\n' % key)
145 > ui.write('- %s\n' % key)
146 > value = params[key]
146 > value = params[key]
147 > if value is not None:
147 > if value is not None:
148 > ui.write(' %s\n' % value)
148 > ui.write(' %s\n' % value)
149 > count = 0
149 > count = 0
150 > for p in unbundler:
150 > for p in unbundler:
151 > count += 1
151 > count += 1
152 > ui.write(' :%s:\n' % p.type)
152 > ui.write(' :%s:\n' % p.type)
153 > ui.write(' mandatory: %i\n' % len(p.mandatoryparams))
153 > ui.write(' mandatory: %i\n' % len(p.mandatoryparams))
154 > ui.write(' advisory: %i\n' % len(p.advisoryparams))
154 > ui.write(' advisory: %i\n' % len(p.advisoryparams))
155 > ui.write(' payload: %i bytes\n' % len(p.data))
155 > ui.write(' payload: %i bytes\n' % len(p.read()))
156 > ui.write('parts count: %i\n' % count)
156 > ui.write('parts count: %i\n' % count)
157 > EOF
157 > EOF
158 $ cat >> $HGRCPATH << EOF
158 $ cat >> $HGRCPATH << EOF
159 > [extensions]
159 > [extensions]
160 > bundle2=$TESTTMP/bundle2.py
160 > bundle2=$TESTTMP/bundle2.py
161 > [server]
161 > [server]
162 > bundle2=True
162 > bundle2=True
163 > EOF
163 > EOF
164
164
165 The extension requires a repo (currently unused)
165 The extension requires a repo (currently unused)
166
166
167 $ hg init main
167 $ hg init main
168 $ cd main
168 $ cd main
169 $ touch a
169 $ touch a
170 $ hg add a
170 $ hg add a
171 $ hg commit -m 'a'
171 $ hg commit -m 'a'
172
172
173
173
174 Empty bundle
174 Empty bundle
175 =================
175 =================
176
176
177 - no option
177 - no option
178 - no parts
178 - no parts
179
179
180 Test bundling
180 Test bundling
181
181
182 $ hg bundle2
182 $ hg bundle2
183 HG20\x00\x00\x00\x00 (no-eol) (esc)
183 HG20\x00\x00\x00\x00 (no-eol) (esc)
184
184
185 Test unbundling
185 Test unbundling
186
186
187 $ hg bundle2 | hg statbundle2
187 $ hg bundle2 | hg statbundle2
188 options count: 0
188 options count: 0
189 parts count: 0
189 parts count: 0
190
190
191 Test old style bundle are detected and refused
191 Test old style bundle are detected and refused
192
192
193 $ hg bundle --all ../bundle.hg
193 $ hg bundle --all ../bundle.hg
194 1 changesets found
194 1 changesets found
195 $ hg statbundle2 < ../bundle.hg
195 $ hg statbundle2 < ../bundle.hg
196 abort: unknown bundle version 10
196 abort: unknown bundle version 10
197 [255]
197 [255]
198
198
199 Test parameters
199 Test parameters
200 =================
200 =================
201
201
202 - some options
202 - some options
203 - no parts
203 - no parts
204
204
205 advisory parameters, no value
205 advisory parameters, no value
206 -------------------------------
206 -------------------------------
207
207
208 Simplest possible parameters form
208 Simplest possible parameters form
209
209
210 Test generation simple option
210 Test generation simple option
211
211
212 $ hg bundle2 --param 'caution'
212 $ hg bundle2 --param 'caution'
213 HG20\x00\x07caution\x00\x00 (no-eol) (esc)
213 HG20\x00\x07caution\x00\x00 (no-eol) (esc)
214
214
215 Test unbundling
215 Test unbundling
216
216
217 $ hg bundle2 --param 'caution' | hg statbundle2
217 $ hg bundle2 --param 'caution' | hg statbundle2
218 options count: 1
218 options count: 1
219 - caution
219 - caution
220 parts count: 0
220 parts count: 0
221
221
222 Test generation multiple option
222 Test generation multiple option
223
223
224 $ hg bundle2 --param 'caution' --param 'meal'
224 $ hg bundle2 --param 'caution' --param 'meal'
225 HG20\x00\x0ccaution meal\x00\x00 (no-eol) (esc)
225 HG20\x00\x0ccaution meal\x00\x00 (no-eol) (esc)
226
226
227 Test unbundling
227 Test unbundling
228
228
229 $ hg bundle2 --param 'caution' --param 'meal' | hg statbundle2
229 $ hg bundle2 --param 'caution' --param 'meal' | hg statbundle2
230 options count: 2
230 options count: 2
231 - caution
231 - caution
232 - meal
232 - meal
233 parts count: 0
233 parts count: 0
234
234
235 advisory parameters, with value
235 advisory parameters, with value
236 -------------------------------
236 -------------------------------
237
237
238 Test generation
238 Test generation
239
239
240 $ hg bundle2 --param 'caution' --param 'meal=vegan' --param 'elephants'
240 $ hg bundle2 --param 'caution' --param 'meal=vegan' --param 'elephants'
241 HG20\x00\x1ccaution meal=vegan elephants\x00\x00 (no-eol) (esc)
241 HG20\x00\x1ccaution meal=vegan elephants\x00\x00 (no-eol) (esc)
242
242
243 Test unbundling
243 Test unbundling
244
244
245 $ hg bundle2 --param 'caution' --param 'meal=vegan' --param 'elephants' | hg statbundle2
245 $ hg bundle2 --param 'caution' --param 'meal=vegan' --param 'elephants' | hg statbundle2
246 options count: 3
246 options count: 3
247 - caution
247 - caution
248 - elephants
248 - elephants
249 - meal
249 - meal
250 vegan
250 vegan
251 parts count: 0
251 parts count: 0
252
252
253 parameter with special char in value
253 parameter with special char in value
254 ---------------------------------------------------
254 ---------------------------------------------------
255
255
256 Test generation
256 Test generation
257
257
258 $ hg bundle2 --param 'e|! 7/=babar%#==tutu' --param simple
258 $ hg bundle2 --param 'e|! 7/=babar%#==tutu' --param simple
259 HG20\x00)e%7C%21%207/=babar%25%23%3D%3Dtutu simple\x00\x00 (no-eol) (esc)
259 HG20\x00)e%7C%21%207/=babar%25%23%3D%3Dtutu simple\x00\x00 (no-eol) (esc)
260
260
261 Test unbundling
261 Test unbundling
262
262
263 $ hg bundle2 --param 'e|! 7/=babar%#==tutu' --param simple | hg statbundle2
263 $ hg bundle2 --param 'e|! 7/=babar%#==tutu' --param simple | hg statbundle2
264 options count: 2
264 options count: 2
265 - e|! 7/
265 - e|! 7/
266 babar%#==tutu
266 babar%#==tutu
267 - simple
267 - simple
268 parts count: 0
268 parts count: 0
269
269
270 Test unknown mandatory option
270 Test unknown mandatory option
271 ---------------------------------------------------
271 ---------------------------------------------------
272
272
273 $ hg bundle2 --param 'Gravity' | hg statbundle2
273 $ hg bundle2 --param 'Gravity' | hg statbundle2
274 abort: unknown parameters: 'Gravity'
274 abort: unknown parameters: 'Gravity'
275 [255]
275 [255]
276
276
277 Test debug output
277 Test debug output
278 ---------------------------------------------------
278 ---------------------------------------------------
279
279
280 bundling debug
280 bundling debug
281
281
282 $ hg bundle2 --debug --param 'e|! 7/=babar%#==tutu' --param simple ../out.hg2
282 $ hg bundle2 --debug --param 'e|! 7/=babar%#==tutu' --param simple ../out.hg2
283 start emission of HG20 stream
283 start emission of HG20 stream
284 bundle parameter: e%7C%21%207/=babar%25%23%3D%3Dtutu simple
284 bundle parameter: e%7C%21%207/=babar%25%23%3D%3Dtutu simple
285 start of parts
285 start of parts
286 end of bundle
286 end of bundle
287
287
288 file content is ok
288 file content is ok
289
289
290 $ cat ../out.hg2
290 $ cat ../out.hg2
291 HG20\x00)e%7C%21%207/=babar%25%23%3D%3Dtutu simple\x00\x00 (no-eol) (esc)
291 HG20\x00)e%7C%21%207/=babar%25%23%3D%3Dtutu simple\x00\x00 (no-eol) (esc)
292
292
293 unbundling debug
293 unbundling debug
294
294
295 $ hg statbundle2 --debug < ../out.hg2
295 $ hg statbundle2 --debug < ../out.hg2
296 start processing of HG20 stream
296 start processing of HG20 stream
297 reading bundle2 stream parameters
297 reading bundle2 stream parameters
298 ignoring unknown parameter 'e|! 7/'
298 ignoring unknown parameter 'e|! 7/'
299 ignoring unknown parameter 'simple'
299 ignoring unknown parameter 'simple'
300 options count: 2
300 options count: 2
301 - e|! 7/
301 - e|! 7/
302 babar%#==tutu
302 babar%#==tutu
303 - simple
303 - simple
304 start extraction of bundle2 parts
304 start extraction of bundle2 parts
305 part header size: 0
305 part header size: 0
306 end of bundle2 stream
306 end of bundle2 stream
307 parts count: 0
307 parts count: 0
308
308
309
309
310 Test buggy input
310 Test buggy input
311 ---------------------------------------------------
311 ---------------------------------------------------
312
312
313 empty parameter name
313 empty parameter name
314
314
315 $ hg bundle2 --param '' --quiet
315 $ hg bundle2 --param '' --quiet
316 abort: empty parameter name
316 abort: empty parameter name
317 [255]
317 [255]
318
318
319 bad parameter name
319 bad parameter name
320
320
321 $ hg bundle2 --param 42babar
321 $ hg bundle2 --param 42babar
322 abort: non letter first character: '42babar'
322 abort: non letter first character: '42babar'
323 [255]
323 [255]
324
324
325
325
326 Test part
326 Test part
327 =================
327 =================
328
328
329 $ hg bundle2 --parts ../parts.hg2 --debug
329 $ hg bundle2 --parts ../parts.hg2 --debug
330 start emission of HG20 stream
330 start emission of HG20 stream
331 bundle parameter:
331 bundle parameter:
332 start of parts
332 start of parts
333 bundle part: "test:empty"
333 bundle part: "test:empty"
334 bundle part: "test:empty"
334 bundle part: "test:empty"
335 bundle part: "test:song"
335 bundle part: "test:song"
336 bundle part: "test:math"
336 bundle part: "test:math"
337 bundle part: "test:ping"
337 bundle part: "test:ping"
338 end of bundle
338 end of bundle
339
339
340 $ cat ../parts.hg2
340 $ cat ../parts.hg2
341 HG20\x00\x00\x00\x11 (esc)
341 HG20\x00\x00\x00\x11 (esc)
342 test:empty\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x11 (esc)
342 test:empty\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x11 (esc)
343 test:empty\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x10 test:song\x00\x00\x00\x02\x00\x00\x00\x00\x00\xb2Patali Dirapata, Cromda Cromda Ripalo, Pata Pata, Ko Ko Ko (esc)
343 test:empty\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x10 test:song\x00\x00\x00\x02\x00\x00\x00\x00\x00\xb2Patali Dirapata, Cromda Cromda Ripalo, Pata Pata, Ko Ko Ko (esc)
344 Bokoro Dipoulito, Rondi Rondi Pepino, Pata Pata, Ko Ko Ko
344 Bokoro Dipoulito, Rondi Rondi Pepino, Pata Pata, Ko Ko Ko
345 Emana Karassoli, Loucra Loucra Ponponto, Pata Pata, Ko Ko Ko.\x00\x00\x00\x00\x00+ test:math\x00\x00\x00\x03\x02\x01\x02\x04\x01\x04\x07\x03pi3.14e2.72cookingraw\x00\x00\x00\x0242\x00\x00\x00\x00\x00\x10 test:ping\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00\x00\x00 (no-eol) (esc)
345 Emana Karassoli, Loucra Loucra Ponponto, Pata Pata, Ko Ko Ko.\x00\x00\x00\x00\x00+ test:math\x00\x00\x00\x03\x02\x01\x02\x04\x01\x04\x07\x03pi3.14e2.72cookingraw\x00\x00\x00\x0242\x00\x00\x00\x00\x00\x10 test:ping\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00\x00\x00 (no-eol) (esc)
346
346
347
347
348 $ hg statbundle2 < ../parts.hg2
348 $ hg statbundle2 < ../parts.hg2
349 options count: 0
349 options count: 0
350 :test:empty:
350 :test:empty:
351 mandatory: 0
351 mandatory: 0
352 advisory: 0
352 advisory: 0
353 payload: 0 bytes
353 payload: 0 bytes
354 :test:empty:
354 :test:empty:
355 mandatory: 0
355 mandatory: 0
356 advisory: 0
356 advisory: 0
357 payload: 0 bytes
357 payload: 0 bytes
358 :test:song:
358 :test:song:
359 mandatory: 0
359 mandatory: 0
360 advisory: 0
360 advisory: 0
361 payload: 178 bytes
361 payload: 178 bytes
362 :test:math:
362 :test:math:
363 mandatory: 2
363 mandatory: 2
364 advisory: 1
364 advisory: 1
365 payload: 2 bytes
365 payload: 2 bytes
366 :test:ping:
366 :test:ping:
367 mandatory: 0
367 mandatory: 0
368 advisory: 0
368 advisory: 0
369 payload: 0 bytes
369 payload: 0 bytes
370 parts count: 5
370 parts count: 5
371
371
372 $ hg statbundle2 --debug < ../parts.hg2
372 $ hg statbundle2 --debug < ../parts.hg2
373 start processing of HG20 stream
373 start processing of HG20 stream
374 reading bundle2 stream parameters
374 reading bundle2 stream parameters
375 options count: 0
375 options count: 0
376 start extraction of bundle2 parts
376 start extraction of bundle2 parts
377 part header size: 17
377 part header size: 17
378 part type: "test:empty"
378 part type: "test:empty"
379 part id: "0"
379 part id: "0"
380 part parameters: 0
380 part parameters: 0
381 payload chunk size: 0
382 :test:empty:
381 :test:empty:
383 mandatory: 0
382 mandatory: 0
384 advisory: 0
383 advisory: 0
384 payload chunk size: 0
385 payload: 0 bytes
385 payload: 0 bytes
386 part header size: 17
386 part header size: 17
387 part type: "test:empty"
387 part type: "test:empty"
388 part id: "1"
388 part id: "1"
389 part parameters: 0
389 part parameters: 0
390 payload chunk size: 0
391 :test:empty:
390 :test:empty:
392 mandatory: 0
391 mandatory: 0
393 advisory: 0
392 advisory: 0
393 payload chunk size: 0
394 payload: 0 bytes
394 payload: 0 bytes
395 part header size: 16
395 part header size: 16
396 part type: "test:song"
396 part type: "test:song"
397 part id: "2"
397 part id: "2"
398 part parameters: 0
398 part parameters: 0
399 payload chunk size: 178
400 payload chunk size: 0
401 :test:song:
399 :test:song:
402 mandatory: 0
400 mandatory: 0
403 advisory: 0
401 advisory: 0
402 payload chunk size: 178
403 payload chunk size: 0
404 payload: 178 bytes
404 payload: 178 bytes
405 part header size: 43
405 part header size: 43
406 part type: "test:math"
406 part type: "test:math"
407 part id: "3"
407 part id: "3"
408 part parameters: 3
408 part parameters: 3
409 payload chunk size: 2
410 payload chunk size: 0
411 :test:math:
409 :test:math:
412 mandatory: 2
410 mandatory: 2
413 advisory: 1
411 advisory: 1
412 payload chunk size: 2
413 payload chunk size: 0
414 payload: 2 bytes
414 payload: 2 bytes
415 part header size: 16
415 part header size: 16
416 part type: "test:ping"
416 part type: "test:ping"
417 part id: "4"
417 part id: "4"
418 part parameters: 0
418 part parameters: 0
419 payload chunk size: 0
420 :test:ping:
419 :test:ping:
421 mandatory: 0
420 mandatory: 0
422 advisory: 0
421 advisory: 0
422 payload chunk size: 0
423 payload: 0 bytes
423 payload: 0 bytes
424 part header size: 0
424 part header size: 0
425 end of bundle2 stream
425 end of bundle2 stream
426 parts count: 5
426 parts count: 5
427
427
428 Test actual unbundling of test part
428 Test actual unbundling of test part
429 =======================================
429 =======================================
430
430
431 Process the bundle
431 Process the bundle
432
432
433 $ hg unbundle2 --debug < ../parts.hg2
433 $ hg unbundle2 --debug < ../parts.hg2
434 start processing of HG20 stream
434 start processing of HG20 stream
435 reading bundle2 stream parameters
435 reading bundle2 stream parameters
436 start extraction of bundle2 parts
436 start extraction of bundle2 parts
437 part header size: 17
437 part header size: 17
438 part type: "test:empty"
438 part type: "test:empty"
439 part id: "0"
439 part id: "0"
440 part parameters: 0
440 part parameters: 0
441 ignoring unknown advisory part 'test:empty'
441 payload chunk size: 0
442 payload chunk size: 0
442 ignoring unknown advisory part 'test:empty'
443 part header size: 17
443 part header size: 17
444 part type: "test:empty"
444 part type: "test:empty"
445 part id: "1"
445 part id: "1"
446 part parameters: 0
446 part parameters: 0
447 ignoring unknown advisory part 'test:empty'
447 payload chunk size: 0
448 payload chunk size: 0
448 ignoring unknown advisory part 'test:empty'
449 part header size: 16
449 part header size: 16
450 part type: "test:song"
450 part type: "test:song"
451 part id: "2"
451 part id: "2"
452 part parameters: 0
452 part parameters: 0
453 found a handler for part 'test:song'
454 The choir starts singing:
453 payload chunk size: 178
455 payload chunk size: 178
454 payload chunk size: 0
456 payload chunk size: 0
455 found a handler for part 'test:song'
456 The choir starts singing:
457 Patali Dirapata, Cromda Cromda Ripalo, Pata Pata, Ko Ko Ko
457 Patali Dirapata, Cromda Cromda Ripalo, Pata Pata, Ko Ko Ko
458 Bokoro Dipoulito, Rondi Rondi Pepino, Pata Pata, Ko Ko Ko
458 Bokoro Dipoulito, Rondi Rondi Pepino, Pata Pata, Ko Ko Ko
459 Emana Karassoli, Loucra Loucra Ponponto, Pata Pata, Ko Ko Ko.
459 Emana Karassoli, Loucra Loucra Ponponto, Pata Pata, Ko Ko Ko.
460 part header size: 43
460 part header size: 43
461 part type: "test:math"
461 part type: "test:math"
462 part id: "3"
462 part id: "3"
463 part parameters: 3
463 part parameters: 3
464 ignoring unknown advisory part 'test:math'
464 payload chunk size: 2
465 payload chunk size: 2
465 payload chunk size: 0
466 payload chunk size: 0
466 ignoring unknown advisory part 'test:math'
467 part header size: 16
467 part header size: 16
468 part type: "test:ping"
468 part type: "test:ping"
469 part id: "4"
469 part id: "4"
470 part parameters: 0
470 part parameters: 0
471 payload chunk size: 0
472 found a handler for part 'test:ping'
471 found a handler for part 'test:ping'
473 received ping request (id 4)
472 received ping request (id 4)
473 payload chunk size: 0
474 part header size: 0
474 part header size: 0
475 end of bundle2 stream
475 end of bundle2 stream
476 0 unread bytes
476 0 unread bytes
477 3 total verses sung
477 3 total verses sung
478
478
479 Unbundle with an unknown mandatory part
479 Unbundle with an unknown mandatory part
480 (should abort)
480 (should abort)
481
481
482 $ hg bundle2 --parts --unknown ../unknown.hg2
482 $ hg bundle2 --parts --unknown ../unknown.hg2
483
483
484 $ hg unbundle2 < ../unknown.hg2
484 $ hg unbundle2 < ../unknown.hg2
485 The choir starts singing:
485 The choir starts singing:
486 Patali Dirapata, Cromda Cromda Ripalo, Pata Pata, Ko Ko Ko
486 Patali Dirapata, Cromda Cromda Ripalo, Pata Pata, Ko Ko Ko
487 Bokoro Dipoulito, Rondi Rondi Pepino, Pata Pata, Ko Ko Ko
487 Bokoro Dipoulito, Rondi Rondi Pepino, Pata Pata, Ko Ko Ko
488 Emana Karassoli, Loucra Loucra Ponponto, Pata Pata, Ko Ko Ko.
488 Emana Karassoli, Loucra Loucra Ponponto, Pata Pata, Ko Ko Ko.
489 0 unread bytes
489 0 unread bytes
490 abort: missing support for 'test:unknown'
490 abort: missing support for 'test:unknown'
491 [255]
491 [255]
492
492
493 unbundle with a reply
493 unbundle with a reply
494
494
495 $ hg unbundle2 ../reply.hg2 < ../parts.hg2
495 $ hg unbundle2 ../reply.hg2 < ../parts.hg2
496 The choir starts singing:
496 The choir starts singing:
497 Patali Dirapata, Cromda Cromda Ripalo, Pata Pata, Ko Ko Ko
497 Patali Dirapata, Cromda Cromda Ripalo, Pata Pata, Ko Ko Ko
498 Bokoro Dipoulito, Rondi Rondi Pepino, Pata Pata, Ko Ko Ko
498 Bokoro Dipoulito, Rondi Rondi Pepino, Pata Pata, Ko Ko Ko
499 Emana Karassoli, Loucra Loucra Ponponto, Pata Pata, Ko Ko Ko.
499 Emana Karassoli, Loucra Loucra Ponponto, Pata Pata, Ko Ko Ko.
500 received ping request (id 4)
500 received ping request (id 4)
501 0 unread bytes
501 0 unread bytes
502 3 total verses sung
502 3 total verses sung
503
503
504 The reply is a bundle
504 The reply is a bundle
505
505
506 $ cat ../reply.hg2
506 $ cat ../reply.hg2
507 HG20\x00\x00\x00\x1e test:pong\x00\x00\x00\x00\x01\x00\x0b\x01in-reply-to4\x00\x00\x00\x00\x00\x00 (no-eol) (esc)
507 HG20\x00\x00\x00\x1e test:pong\x00\x00\x00\x00\x01\x00\x0b\x01in-reply-to4\x00\x00\x00\x00\x00\x00 (no-eol) (esc)
508
508
509 The reply is valid
509 The reply is valid
510
510
511 $ hg statbundle2 < ../reply.hg2
511 $ hg statbundle2 < ../reply.hg2
512 options count: 0
512 options count: 0
513 :test:pong:
513 :test:pong:
514 mandatory: 1
514 mandatory: 1
515 advisory: 0
515 advisory: 0
516 payload: 0 bytes
516 payload: 0 bytes
517 parts count: 1
517 parts count: 1
518
518
519 Support for changegroup
519 Support for changegroup
520 ===================================
520 ===================================
521
521
522 $ hg unbundle $TESTDIR/bundles/rebase.hg
522 $ hg unbundle $TESTDIR/bundles/rebase.hg
523 adding changesets
523 adding changesets
524 adding manifests
524 adding manifests
525 adding file changes
525 adding file changes
526 added 8 changesets with 7 changes to 7 files (+3 heads)
526 added 8 changesets with 7 changes to 7 files (+3 heads)
527 (run 'hg heads' to see heads, 'hg merge' to merge)
527 (run 'hg heads' to see heads, 'hg merge' to merge)
528
528
529 $ hg log -G
529 $ hg log -G
530 o changeset: 8:02de42196ebe
530 o changeset: 8:02de42196ebe
531 | tag: tip
531 | tag: tip
532 | parent: 6:24b6387c8c8c
532 | parent: 6:24b6387c8c8c
533 | user: Nicolas Dumazet <nicdumz.commits@gmail.com>
533 | user: Nicolas Dumazet <nicdumz.commits@gmail.com>
534 | date: Sat Apr 30 15:24:48 2011 +0200
534 | date: Sat Apr 30 15:24:48 2011 +0200
535 | summary: H
535 | summary: H
536 |
536 |
537 | o changeset: 7:eea13746799a
537 | o changeset: 7:eea13746799a
538 |/| parent: 6:24b6387c8c8c
538 |/| parent: 6:24b6387c8c8c
539 | | parent: 5:9520eea781bc
539 | | parent: 5:9520eea781bc
540 | | user: Nicolas Dumazet <nicdumz.commits@gmail.com>
540 | | user: Nicolas Dumazet <nicdumz.commits@gmail.com>
541 | | date: Sat Apr 30 15:24:48 2011 +0200
541 | | date: Sat Apr 30 15:24:48 2011 +0200
542 | | summary: G
542 | | summary: G
543 | |
543 | |
544 o | changeset: 6:24b6387c8c8c
544 o | changeset: 6:24b6387c8c8c
545 | | parent: 1:cd010b8cd998
545 | | parent: 1:cd010b8cd998
546 | | user: Nicolas Dumazet <nicdumz.commits@gmail.com>
546 | | user: Nicolas Dumazet <nicdumz.commits@gmail.com>
547 | | date: Sat Apr 30 15:24:48 2011 +0200
547 | | date: Sat Apr 30 15:24:48 2011 +0200
548 | | summary: F
548 | | summary: F
549 | |
549 | |
550 | o changeset: 5:9520eea781bc
550 | o changeset: 5:9520eea781bc
551 |/ parent: 1:cd010b8cd998
551 |/ parent: 1:cd010b8cd998
552 | user: Nicolas Dumazet <nicdumz.commits@gmail.com>
552 | user: Nicolas Dumazet <nicdumz.commits@gmail.com>
553 | date: Sat Apr 30 15:24:48 2011 +0200
553 | date: Sat Apr 30 15:24:48 2011 +0200
554 | summary: E
554 | summary: E
555 |
555 |
556 | o changeset: 4:32af7686d403
556 | o changeset: 4:32af7686d403
557 | | user: Nicolas Dumazet <nicdumz.commits@gmail.com>
557 | | user: Nicolas Dumazet <nicdumz.commits@gmail.com>
558 | | date: Sat Apr 30 15:24:48 2011 +0200
558 | | date: Sat Apr 30 15:24:48 2011 +0200
559 | | summary: D
559 | | summary: D
560 | |
560 | |
561 | o changeset: 3:5fddd98957c8
561 | o changeset: 3:5fddd98957c8
562 | | user: Nicolas Dumazet <nicdumz.commits@gmail.com>
562 | | user: Nicolas Dumazet <nicdumz.commits@gmail.com>
563 | | date: Sat Apr 30 15:24:48 2011 +0200
563 | | date: Sat Apr 30 15:24:48 2011 +0200
564 | | summary: C
564 | | summary: C
565 | |
565 | |
566 | o changeset: 2:42ccdea3bb16
566 | o changeset: 2:42ccdea3bb16
567 |/ user: Nicolas Dumazet <nicdumz.commits@gmail.com>
567 |/ user: Nicolas Dumazet <nicdumz.commits@gmail.com>
568 | date: Sat Apr 30 15:24:48 2011 +0200
568 | date: Sat Apr 30 15:24:48 2011 +0200
569 | summary: B
569 | summary: B
570 |
570 |
571 o changeset: 1:cd010b8cd998
571 o changeset: 1:cd010b8cd998
572 parent: -1:000000000000
572 parent: -1:000000000000
573 user: Nicolas Dumazet <nicdumz.commits@gmail.com>
573 user: Nicolas Dumazet <nicdumz.commits@gmail.com>
574 date: Sat Apr 30 15:24:48 2011 +0200
574 date: Sat Apr 30 15:24:48 2011 +0200
575 summary: A
575 summary: A
576
576
577 @ changeset: 0:3903775176ed
577 @ changeset: 0:3903775176ed
578 user: test
578 user: test
579 date: Thu Jan 01 00:00:00 1970 +0000
579 date: Thu Jan 01 00:00:00 1970 +0000
580 summary: a
580 summary: a
581
581
582
582
583 $ hg bundle2 --debug --rev '8+7+5+4' ../rev.hg2
583 $ hg bundle2 --debug --rev '8+7+5+4' ../rev.hg2
584 4 changesets found
584 4 changesets found
585 list of changesets:
585 list of changesets:
586 32af7686d403cf45b5d95f2d70cebea587ac806a
586 32af7686d403cf45b5d95f2d70cebea587ac806a
587 9520eea781bcca16c1e15acc0ba14335a0e8e5ba
587 9520eea781bcca16c1e15acc0ba14335a0e8e5ba
588 eea13746799a9e0bfd88f29d3c2e9dc9389f524f
588 eea13746799a9e0bfd88f29d3c2e9dc9389f524f
589 02de42196ebee42ef284b6780a87cdc96e8eaab6
589 02de42196ebee42ef284b6780a87cdc96e8eaab6
590 start emission of HG20 stream
590 start emission of HG20 stream
591 bundle parameter:
591 bundle parameter:
592 start of parts
592 start of parts
593 bundle part: "changegroup"
593 bundle part: "changegroup"
594 bundling: 1/4 changesets (25.00%)
594 bundling: 1/4 changesets (25.00%)
595 bundling: 2/4 changesets (50.00%)
595 bundling: 2/4 changesets (50.00%)
596 bundling: 3/4 changesets (75.00%)
596 bundling: 3/4 changesets (75.00%)
597 bundling: 4/4 changesets (100.00%)
597 bundling: 4/4 changesets (100.00%)
598 bundling: 1/4 manifests (25.00%)
598 bundling: 1/4 manifests (25.00%)
599 bundling: 2/4 manifests (50.00%)
599 bundling: 2/4 manifests (50.00%)
600 bundling: 3/4 manifests (75.00%)
600 bundling: 3/4 manifests (75.00%)
601 bundling: 4/4 manifests (100.00%)
601 bundling: 4/4 manifests (100.00%)
602 bundling: D 1/3 files (33.33%)
602 bundling: D 1/3 files (33.33%)
603 bundling: E 2/3 files (66.67%)
603 bundling: E 2/3 files (66.67%)
604 bundling: H 3/3 files (100.00%)
604 bundling: H 3/3 files (100.00%)
605 end of bundle
605 end of bundle
606
606
607 $ cat ../rev.hg2
607 $ cat ../rev.hg2
608 HG20\x00\x00\x00\x12\x0bchangegroup\x00\x00\x00\x00\x00\x00\x00\x00\x06\x19HG10UN\x00\x00\x00\xa42\xafv\x86\xd4\x03\xcfE\xb5\xd9_-p\xce\xbe\xa5\x87\xac\x80j_\xdd\xd9\x89W\xc8\xa5JMCm\xfe\x1d\xa9\xd8\x7f!\xa1\xb9{\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x002\xafv\x86\xd4\x03\xcfE\xb5\xd9_-p\xce\xbe\xa5\x87\xac\x80j\x00\x00\x00\x00\x00\x00\x00)\x00\x00\x00)6e1f4c47ecb533ffd0c8e52cdc88afb6cd39e20c (esc)
608 HG20\x00\x00\x00\x12\x0bchangegroup\x00\x00\x00\x00\x00\x00\x00\x00\x06\x19HG10UN\x00\x00\x00\xa42\xafv\x86\xd4\x03\xcfE\xb5\xd9_-p\xce\xbe\xa5\x87\xac\x80j_\xdd\xd9\x89W\xc8\xa5JMCm\xfe\x1d\xa9\xd8\x7f!\xa1\xb9{\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x002\xafv\x86\xd4\x03\xcfE\xb5\xd9_-p\xce\xbe\xa5\x87\xac\x80j\x00\x00\x00\x00\x00\x00\x00)\x00\x00\x00)6e1f4c47ecb533ffd0c8e52cdc88afb6cd39e20c (esc)
609 \x00\x00\x00f\x00\x00\x00h\x00\x00\x00\x02D (esc)
609 \x00\x00\x00f\x00\x00\x00h\x00\x00\x00\x02D (esc)
610 \x00\x00\x00i\x00\x00\x00j\x00\x00\x00\x01D\x00\x00\x00\xa4\x95 \xee\xa7\x81\xbc\xca\x16\xc1\xe1Z\xcc\x0b\xa1C5\xa0\xe8\xe5\xba\xcd\x01\x0b\x8c\xd9\x98\xf3\x98\x1aZ\x81\x15\xf9O\x8d\xa4\xabP`\x89\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x95 \xee\xa7\x81\xbc\xca\x16\xc1\xe1Z\xcc\x0b\xa1C5\xa0\xe8\xe5\xba\x00\x00\x00\x00\x00\x00\x00)\x00\x00\x00)4dece9c826f69490507b98c6383a3009b295837d (esc)
610 \x00\x00\x00i\x00\x00\x00j\x00\x00\x00\x01D\x00\x00\x00\xa4\x95 \xee\xa7\x81\xbc\xca\x16\xc1\xe1Z\xcc\x0b\xa1C5\xa0\xe8\xe5\xba\xcd\x01\x0b\x8c\xd9\x98\xf3\x98\x1aZ\x81\x15\xf9O\x8d\xa4\xabP`\x89\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x95 \xee\xa7\x81\xbc\xca\x16\xc1\xe1Z\xcc\x0b\xa1C5\xa0\xe8\xe5\xba\x00\x00\x00\x00\x00\x00\x00)\x00\x00\x00)4dece9c826f69490507b98c6383a3009b295837d (esc)
611 \x00\x00\x00f\x00\x00\x00h\x00\x00\x00\x02E (esc)
611 \x00\x00\x00f\x00\x00\x00h\x00\x00\x00\x02E (esc)
612 \x00\x00\x00i\x00\x00\x00j\x00\x00\x00\x01E\x00\x00\x00\xa2\xee\xa17Fy\x9a\x9e\x0b\xfd\x88\xf2\x9d<.\x9d\xc98\x9fRO$\xb68|\x8c\x8c\xae7\x17\x88\x80\xf3\xfa\x95\xde\xd3\xcb\x1c\xf7\x85\x95 \xee\xa7\x81\xbc\xca\x16\xc1\xe1Z\xcc\x0b\xa1C5\xa0\xe8\xe5\xba\xee\xa17Fy\x9a\x9e\x0b\xfd\x88\xf2\x9d<.\x9d\xc98\x9fRO\x00\x00\x00\x00\x00\x00\x00)\x00\x00\x00)365b93d57fdf4814e2b5911d6bacff2b12014441 (esc)
612 \x00\x00\x00i\x00\x00\x00j\x00\x00\x00\x01E\x00\x00\x00\xa2\xee\xa17Fy\x9a\x9e\x0b\xfd\x88\xf2\x9d<.\x9d\xc98\x9fRO$\xb68|\x8c\x8c\xae7\x17\x88\x80\xf3\xfa\x95\xde\xd3\xcb\x1c\xf7\x85\x95 \xee\xa7\x81\xbc\xca\x16\xc1\xe1Z\xcc\x0b\xa1C5\xa0\xe8\xe5\xba\xee\xa17Fy\x9a\x9e\x0b\xfd\x88\xf2\x9d<.\x9d\xc98\x9fRO\x00\x00\x00\x00\x00\x00\x00)\x00\x00\x00)365b93d57fdf4814e2b5911d6bacff2b12014441 (esc)
613 \x00\x00\x00f\x00\x00\x00h\x00\x00\x00\x00\x00\x00\x00i\x00\x00\x00j\x00\x00\x00\x01G\x00\x00\x00\xa4\x02\xdeB\x19n\xbe\xe4.\xf2\x84\xb6x (esc)
613 \x00\x00\x00f\x00\x00\x00h\x00\x00\x00\x00\x00\x00\x00i\x00\x00\x00j\x00\x00\x00\x01G\x00\x00\x00\xa4\x02\xdeB\x19n\xbe\xe4.\xf2\x84\xb6x (esc)
614 \x87\xcd\xc9n\x8e\xaa\xb6$\xb68|\x8c\x8c\xae7\x17\x88\x80\xf3\xfa\x95\xde\xd3\xcb\x1c\xf7\x85\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\xdeB\x19n\xbe\xe4.\xf2\x84\xb6x (esc)
614 \x87\xcd\xc9n\x8e\xaa\xb6$\xb68|\x8c\x8c\xae7\x17\x88\x80\xf3\xfa\x95\xde\xd3\xcb\x1c\xf7\x85\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\xdeB\x19n\xbe\xe4.\xf2\x84\xb6x (esc)
615 \x87\xcd\xc9n\x8e\xaa\xb6\x00\x00\x00\x00\x00\x00\x00)\x00\x00\x00)8bee48edc7318541fc0013ee41b089276a8c24bf (esc)
615 \x87\xcd\xc9n\x8e\xaa\xb6\x00\x00\x00\x00\x00\x00\x00)\x00\x00\x00)8bee48edc7318541fc0013ee41b089276a8c24bf (esc)
616 \x00\x00\x00f\x00\x00\x00f\x00\x00\x00\x02H (esc)
616 \x00\x00\x00f\x00\x00\x00f\x00\x00\x00\x02H (esc)
617 \x00\x00\x00g\x00\x00\x00h\x00\x00\x00\x01H\x00\x00\x00\x00\x00\x00\x00\x8bn\x1fLG\xec\xb53\xff\xd0\xc8\xe5,\xdc\x88\xaf\xb6\xcd9\xe2\x0cf\xa5\xa0\x18\x17\xfd\xf5#\x9c'8\x02\xb5\xb7a\x8d\x05\x1c\x89\xe4\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x002\xafv\x86\xd4\x03\xcfE\xb5\xd9_-p\xce\xbe\xa5\x87\xac\x80j\x00\x00\x00\x81\x00\x00\x00\x81\x00\x00\x00+D\x00c3f1ca2924c16a19b0656a84900e504e5b0aec2d (esc)
617 \x00\x00\x00g\x00\x00\x00h\x00\x00\x00\x01H\x00\x00\x00\x00\x00\x00\x00\x8bn\x1fLG\xec\xb53\xff\xd0\xc8\xe5,\xdc\x88\xaf\xb6\xcd9\xe2\x0cf\xa5\xa0\x18\x17\xfd\xf5#\x9c'8\x02\xb5\xb7a\x8d\x05\x1c\x89\xe4\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x002\xafv\x86\xd4\x03\xcfE\xb5\xd9_-p\xce\xbe\xa5\x87\xac\x80j\x00\x00\x00\x81\x00\x00\x00\x81\x00\x00\x00+D\x00c3f1ca2924c16a19b0656a84900e504e5b0aec2d (esc)
618 \x00\x00\x00\x8bM\xec\xe9\xc8&\xf6\x94\x90P{\x98\xc68:0 \xb2\x95\x83}\x00}\x8c\x9d\x88\x84\x13%\xf5\xc6\xb0cq\xb3[N\x8a+\x1a\x83\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x95 \xee\xa7\x81\xbc\xca\x16\xc1\xe1Z\xcc\x0b\xa1C5\xa0\xe8\xe5\xba\x00\x00\x00+\x00\x00\x00\xac\x00\x00\x00+E\x009c6fd0350a6c0d0c49d4a9c5017cf07043f54e58 (esc)
618 \x00\x00\x00\x8bM\xec\xe9\xc8&\xf6\x94\x90P{\x98\xc68:0 \xb2\x95\x83}\x00}\x8c\x9d\x88\x84\x13%\xf5\xc6\xb0cq\xb3[N\x8a+\x1a\x83\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x95 \xee\xa7\x81\xbc\xca\x16\xc1\xe1Z\xcc\x0b\xa1C5\xa0\xe8\xe5\xba\x00\x00\x00+\x00\x00\x00\xac\x00\x00\x00+E\x009c6fd0350a6c0d0c49d4a9c5017cf07043f54e58 (esc)
619 \x00\x00\x00\x8b6[\x93\xd5\x7f\xdfH\x14\xe2\xb5\x91\x1dk\xac\xff+\x12\x01DA(\xa5\x84\xc6^\xf1!\xf8\x9e\xb6j\xb7\xd0\xbc\x15=\x80\x99\xe7\xceM\xec\xe9\xc8&\xf6\x94\x90P{\x98\xc68:0 \xb2\x95\x83}\xee\xa17Fy\x9a\x9e\x0b\xfd\x88\xf2\x9d<.\x9d\xc98\x9fRO\x00\x00\x00V\x00\x00\x00V\x00\x00\x00+F\x0022bfcfd62a21a3287edbd4d656218d0f525ed76a (esc)
619 \x00\x00\x00\x8b6[\x93\xd5\x7f\xdfH\x14\xe2\xb5\x91\x1dk\xac\xff+\x12\x01DA(\xa5\x84\xc6^\xf1!\xf8\x9e\xb6j\xb7\xd0\xbc\x15=\x80\x99\xe7\xceM\xec\xe9\xc8&\xf6\x94\x90P{\x98\xc68:0 \xb2\x95\x83}\xee\xa17Fy\x9a\x9e\x0b\xfd\x88\xf2\x9d<.\x9d\xc98\x9fRO\x00\x00\x00V\x00\x00\x00V\x00\x00\x00+F\x0022bfcfd62a21a3287edbd4d656218d0f525ed76a (esc)
620 \x00\x00\x00\x97\x8b\xeeH\xed\xc71\x85A\xfc\x00\x13\xeeA\xb0\x89'j\x8c$\xbf(\xa5\x84\xc6^\xf1!\xf8\x9e\xb6j\xb7\xd0\xbc\x15=\x80\x99\xe7\xce\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\xdeB\x19n\xbe\xe4.\xf2\x84\xb6x (esc)
620 \x00\x00\x00\x97\x8b\xeeH\xed\xc71\x85A\xfc\x00\x13\xeeA\xb0\x89'j\x8c$\xbf(\xa5\x84\xc6^\xf1!\xf8\x9e\xb6j\xb7\xd0\xbc\x15=\x80\x99\xe7\xce\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\xdeB\x19n\xbe\xe4.\xf2\x84\xb6x (esc)
621 \x87\xcd\xc9n\x8e\xaa\xb6\x00\x00\x00+\x00\x00\x00V\x00\x00\x00\x00\x00\x00\x00\x81\x00\x00\x00\x81\x00\x00\x00+H\x008500189e74a9e0475e822093bc7db0d631aeb0b4 (esc)
621 \x87\xcd\xc9n\x8e\xaa\xb6\x00\x00\x00+\x00\x00\x00V\x00\x00\x00\x00\x00\x00\x00\x81\x00\x00\x00\x81\x00\x00\x00+H\x008500189e74a9e0475e822093bc7db0d631aeb0b4 (esc)
622 \x00\x00\x00\x00\x00\x00\x00\x05D\x00\x00\x00b\xc3\xf1\xca)$\xc1j\x19\xb0ej\x84\x90\x0ePN[ (esc)
622 \x00\x00\x00\x00\x00\x00\x00\x05D\x00\x00\x00b\xc3\xf1\xca)$\xc1j\x19\xb0ej\x84\x90\x0ePN[ (esc)
623 \xec-\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x002\xafv\x86\xd4\x03\xcfE\xb5\xd9_-p\xce\xbe\xa5\x87\xac\x80j\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02D (esc)
623 \xec-\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x002\xafv\x86\xd4\x03\xcfE\xb5\xd9_-p\xce\xbe\xa5\x87\xac\x80j\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02D (esc)
624 \x00\x00\x00\x00\x00\x00\x00\x05E\x00\x00\x00b\x9co\xd05 (esc)
624 \x00\x00\x00\x00\x00\x00\x00\x05E\x00\x00\x00b\x9co\xd05 (esc)
625 l\r (no-eol) (esc)
625 l\r (no-eol) (esc)
626 \x0cI\xd4\xa9\xc5\x01|\xf0pC\xf5NX\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x95 \xee\xa7\x81\xbc\xca\x16\xc1\xe1Z\xcc\x0b\xa1C5\xa0\xe8\xe5\xba\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02E (esc)
626 \x0cI\xd4\xa9\xc5\x01|\xf0pC\xf5NX\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x95 \xee\xa7\x81\xbc\xca\x16\xc1\xe1Z\xcc\x0b\xa1C5\xa0\xe8\xe5\xba\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02E (esc)
627 \x00\x00\x00\x00\x00\x00\x00\x05H\x00\x00\x00b\x85\x00\x18\x9et\xa9\xe0G^\x82 \x93\xbc}\xb0\xd61\xae\xb0\xb4\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\xdeB\x19n\xbe\xe4.\xf2\x84\xb6x (esc)
627 \x00\x00\x00\x00\x00\x00\x00\x05H\x00\x00\x00b\x85\x00\x18\x9et\xa9\xe0G^\x82 \x93\xbc}\xb0\xd61\xae\xb0\xb4\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\xdeB\x19n\xbe\xe4.\xf2\x84\xb6x (esc)
628 \x87\xcd\xc9n\x8e\xaa\xb6\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02H (esc)
628 \x87\xcd\xc9n\x8e\xaa\xb6\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02H (esc)
629 \x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00 (no-eol) (esc)
629 \x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00 (no-eol) (esc)
630
630
631 $ hg unbundle2 ../rev-replay.hg2 < ../rev.hg2
631 $ hg unbundle2 ../rev-replay.hg2 < ../rev.hg2
632 adding changesets
632 adding changesets
633 adding manifests
633 adding manifests
634 adding file changes
634 adding file changes
635 added 0 changesets with 0 changes to 3 files
635 added 0 changesets with 0 changes to 3 files
636 0 unread bytes
636 0 unread bytes
637 addchangegroup return: 1
637 addchangegroup return: 1
638
638
639 $ cat ../rev-replay.hg2
639 $ cat ../rev-replay.hg2
640 HG20\x00\x00\x00/\x11reply:changegroup\x00\x00\x00\x00\x00\x02\x0b\x01\x06\x01in-reply-to0return1\x00\x00\x00\x00\x00\x00 (no-eol) (esc)
640 HG20\x00\x00\x00/\x11reply:changegroup\x00\x00\x00\x00\x00\x02\x0b\x01\x06\x01in-reply-to0return1\x00\x00\x00\x00\x00\x00 (no-eol) (esc)
641
641
642 Real world exchange
642 Real world exchange
643 =====================
643 =====================
644
644
645
645
646 clone --pull
646 clone --pull
647
647
648 $ cd ..
648 $ cd ..
649 $ hg clone main other --pull --rev 9520eea781bc
649 $ hg clone main other --pull --rev 9520eea781bc
650 adding changesets
650 adding changesets
651 adding manifests
651 adding manifests
652 adding file changes
652 adding file changes
653 added 2 changesets with 2 changes to 2 files
653 added 2 changesets with 2 changes to 2 files
654 updating to branch default
654 updating to branch default
655 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
655 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
656 $ hg -R other log -G
656 $ hg -R other log -G
657 @ changeset: 1:9520eea781bc
657 @ changeset: 1:9520eea781bc
658 | tag: tip
658 | tag: tip
659 | user: Nicolas Dumazet <nicdumz.commits@gmail.com>
659 | user: Nicolas Dumazet <nicdumz.commits@gmail.com>
660 | date: Sat Apr 30 15:24:48 2011 +0200
660 | date: Sat Apr 30 15:24:48 2011 +0200
661 | summary: E
661 | summary: E
662 |
662 |
663 o changeset: 0:cd010b8cd998
663 o changeset: 0:cd010b8cd998
664 user: Nicolas Dumazet <nicdumz.commits@gmail.com>
664 user: Nicolas Dumazet <nicdumz.commits@gmail.com>
665 date: Sat Apr 30 15:24:48 2011 +0200
665 date: Sat Apr 30 15:24:48 2011 +0200
666 summary: A
666 summary: A
667
667
668
668
669 pull
669 pull
670
670
671 $ hg -R other pull
671 $ hg -R other pull
672 pulling from $TESTTMP/main (glob)
672 pulling from $TESTTMP/main (glob)
673 searching for changes
673 searching for changes
674 adding changesets
674 adding changesets
675 adding manifests
675 adding manifests
676 adding file changes
676 adding file changes
677 added 7 changesets with 6 changes to 6 files (+3 heads)
677 added 7 changesets with 6 changes to 6 files (+3 heads)
678 (run 'hg heads' to see heads, 'hg merge' to merge)
678 (run 'hg heads' to see heads, 'hg merge' to merge)
General Comments 0
You need to be logged in to leave comments. Login now