##// END OF EJS Templates
push: add "remote" to 'repository changed while pushing' messages (issue5971)...
Taapas Agrawal -
r40182:58ebf508 default
parent child Browse files
Show More
@@ -1,2302 +1,2302
1 1 # bundle2.py - generic container format to transmit arbitrary data.
2 2 #
3 3 # Copyright 2013 Facebook, Inc.
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2 or any later version.
7 7 """Handling of the new bundle2 format
8 8
9 9 The goal of bundle2 is to act as an atomically packet to transmit a set of
10 10 payloads in an application agnostic way. It consist in a sequence of "parts"
11 11 that will be handed to and processed by the application layer.
12 12
13 13
14 14 General format architecture
15 15 ===========================
16 16
17 17 The format is architectured as follow
18 18
19 19 - magic string
20 20 - stream level parameters
21 21 - payload parts (any number)
22 22 - end of stream marker.
23 23
24 24 the Binary format
25 25 ============================
26 26
27 27 All numbers are unsigned and big-endian.
28 28
29 29 stream level parameters
30 30 ------------------------
31 31
32 32 Binary format is as follow
33 33
34 34 :params size: int32
35 35
36 36 The total number of Bytes used by the parameters
37 37
38 38 :params value: arbitrary number of Bytes
39 39
40 40 A blob of `params size` containing the serialized version of all stream level
41 41 parameters.
42 42
43 43 The blob contains a space separated list of parameters. Parameters with value
44 44 are stored in the form `<name>=<value>`. Both name and value are urlquoted.
45 45
46 46 Empty name are obviously forbidden.
47 47
48 48 Name MUST start with a letter. If this first letter is lower case, the
49 49 parameter is advisory and can be safely ignored. However when the first
50 50 letter is capital, the parameter is mandatory and the bundling process MUST
51 51 stop if he is not able to proceed it.
52 52
53 53 Stream parameters use a simple textual format for two main reasons:
54 54
55 55 - Stream level parameters should remain simple and we want to discourage any
56 56 crazy usage.
57 57 - Textual data allow easy human inspection of a bundle2 header in case of
58 58 troubles.
59 59
60 60 Any Applicative level options MUST go into a bundle2 part instead.
61 61
62 62 Payload part
63 63 ------------------------
64 64
65 65 Binary format is as follow
66 66
67 67 :header size: int32
68 68
69 69 The total number of Bytes used by the part header. When the header is empty
70 70 (size = 0) this is interpreted as the end of stream marker.
71 71
72 72 :header:
73 73
74 74 The header defines how to interpret the part. It contains two piece of
75 75 data: the part type, and the part parameters.
76 76
77 77 The part type is used to route an application level handler, that can
78 78 interpret payload.
79 79
80 80 Part parameters are passed to the application level handler. They are
81 81 meant to convey information that will help the application level object to
82 82 interpret the part payload.
83 83
84 84 The binary format of the header is has follow
85 85
86 86 :typesize: (one byte)
87 87
88 88 :parttype: alphanumerical part name (restricted to [a-zA-Z0-9_:-]*)
89 89
90 90 :partid: A 32bits integer (unique in the bundle) that can be used to refer
91 91 to this part.
92 92
93 93 :parameters:
94 94
95 95 Part's parameter may have arbitrary content, the binary structure is::
96 96
97 97 <mandatory-count><advisory-count><param-sizes><param-data>
98 98
99 99 :mandatory-count: 1 byte, number of mandatory parameters
100 100
101 101 :advisory-count: 1 byte, number of advisory parameters
102 102
103 103 :param-sizes:
104 104
105 105 N couple of bytes, where N is the total number of parameters. Each
106 106 couple contains (<size-of-key>, <size-of-value) for one parameter.
107 107
108 108 :param-data:
109 109
110 110 A blob of bytes from which each parameter key and value can be
111 111 retrieved using the list of size couples stored in the previous
112 112 field.
113 113
114 114 Mandatory parameters comes first, then the advisory ones.
115 115
116 116 Each parameter's key MUST be unique within the part.
117 117
118 118 :payload:
119 119
120 120 payload is a series of `<chunksize><chunkdata>`.
121 121
122 122 `chunksize` is an int32, `chunkdata` are plain bytes (as much as
123 123 `chunksize` says)` The payload part is concluded by a zero size chunk.
124 124
125 125 The current implementation always produces either zero or one chunk.
126 126 This is an implementation limitation that will ultimately be lifted.
127 127
128 128 `chunksize` can be negative to trigger special case processing. No such
129 129 processing is in place yet.
130 130
131 131 Bundle processing
132 132 ============================
133 133
134 134 Each part is processed in order using a "part handler". Handler are registered
135 135 for a certain part type.
136 136
137 137 The matching of a part to its handler is case insensitive. The case of the
138 138 part type is used to know if a part is mandatory or advisory. If the Part type
139 139 contains any uppercase char it is considered mandatory. When no handler is
140 140 known for a Mandatory part, the process is aborted and an exception is raised.
141 141 If the part is advisory and no handler is known, the part is ignored. When the
142 142 process is aborted, the full bundle is still read from the stream to keep the
143 143 channel usable. But none of the part read from an abort are processed. In the
144 144 future, dropping the stream may become an option for channel we do not care to
145 145 preserve.
146 146 """
147 147
148 148 from __future__ import absolute_import, division
149 149
150 150 import collections
151 151 import errno
152 152 import os
153 153 import re
154 154 import string
155 155 import struct
156 156 import sys
157 157
158 158 from .i18n import _
159 159 from . import (
160 160 bookmarks,
161 161 changegroup,
162 162 encoding,
163 163 error,
164 164 node as nodemod,
165 165 obsolete,
166 166 phases,
167 167 pushkey,
168 168 pycompat,
169 169 streamclone,
170 170 tags,
171 171 url,
172 172 util,
173 173 )
174 174 from .utils import (
175 175 stringutil,
176 176 )
177 177
178 178 urlerr = util.urlerr
179 179 urlreq = util.urlreq
180 180
181 181 _pack = struct.pack
182 182 _unpack = struct.unpack
183 183
184 184 _fstreamparamsize = '>i'
185 185 _fpartheadersize = '>i'
186 186 _fparttypesize = '>B'
187 187 _fpartid = '>I'
188 188 _fpayloadsize = '>i'
189 189 _fpartparamcount = '>BB'
190 190
191 191 preferedchunksize = 32768
192 192
193 193 _parttypeforbidden = re.compile('[^a-zA-Z0-9_:-]')
194 194
195 195 def outdebug(ui, message):
196 196 """debug regarding output stream (bundling)"""
197 197 if ui.configbool('devel', 'bundle2.debug'):
198 198 ui.debug('bundle2-output: %s\n' % message)
199 199
200 200 def indebug(ui, message):
201 201 """debug on input stream (unbundling)"""
202 202 if ui.configbool('devel', 'bundle2.debug'):
203 203 ui.debug('bundle2-input: %s\n' % message)
204 204
205 205 def validateparttype(parttype):
206 206 """raise ValueError if a parttype contains invalid character"""
207 207 if _parttypeforbidden.search(parttype):
208 208 raise ValueError(parttype)
209 209
210 210 def _makefpartparamsizes(nbparams):
211 211 """return a struct format to read part parameter sizes
212 212
213 213 The number parameters is variable so we need to build that format
214 214 dynamically.
215 215 """
216 216 return '>'+('BB'*nbparams)
217 217
218 218 parthandlermapping = {}
219 219
220 220 def parthandler(parttype, params=()):
221 221 """decorator that register a function as a bundle2 part handler
222 222
223 223 eg::
224 224
225 225 @parthandler('myparttype', ('mandatory', 'param', 'handled'))
226 226 def myparttypehandler(...):
227 227 '''process a part of type "my part".'''
228 228 ...
229 229 """
230 230 validateparttype(parttype)
231 231 def _decorator(func):
232 232 lparttype = parttype.lower() # enforce lower case matching.
233 233 assert lparttype not in parthandlermapping
234 234 parthandlermapping[lparttype] = func
235 235 func.params = frozenset(params)
236 236 return func
237 237 return _decorator
238 238
239 239 class unbundlerecords(object):
240 240 """keep record of what happens during and unbundle
241 241
242 242 New records are added using `records.add('cat', obj)`. Where 'cat' is a
243 243 category of record and obj is an arbitrary object.
244 244
245 245 `records['cat']` will return all entries of this category 'cat'.
246 246
247 247 Iterating on the object itself will yield `('category', obj)` tuples
248 248 for all entries.
249 249
250 250 All iterations happens in chronological order.
251 251 """
252 252
253 253 def __init__(self):
254 254 self._categories = {}
255 255 self._sequences = []
256 256 self._replies = {}
257 257
258 258 def add(self, category, entry, inreplyto=None):
259 259 """add a new record of a given category.
260 260
261 261 The entry can then be retrieved in the list returned by
262 262 self['category']."""
263 263 self._categories.setdefault(category, []).append(entry)
264 264 self._sequences.append((category, entry))
265 265 if inreplyto is not None:
266 266 self.getreplies(inreplyto).add(category, entry)
267 267
268 268 def getreplies(self, partid):
269 269 """get the records that are replies to a specific part"""
270 270 return self._replies.setdefault(partid, unbundlerecords())
271 271
272 272 def __getitem__(self, cat):
273 273 return tuple(self._categories.get(cat, ()))
274 274
275 275 def __iter__(self):
276 276 return iter(self._sequences)
277 277
278 278 def __len__(self):
279 279 return len(self._sequences)
280 280
281 281 def __nonzero__(self):
282 282 return bool(self._sequences)
283 283
284 284 __bool__ = __nonzero__
285 285
286 286 class bundleoperation(object):
287 287 """an object that represents a single bundling process
288 288
289 289 Its purpose is to carry unbundle-related objects and states.
290 290
291 291 A new object should be created at the beginning of each bundle processing.
292 292 The object is to be returned by the processing function.
293 293
294 294 The object has very little content now it will ultimately contain:
295 295 * an access to the repo the bundle is applied to,
296 296 * a ui object,
297 297 * a way to retrieve a transaction to add changes to the repo,
298 298 * a way to record the result of processing each part,
299 299 * a way to construct a bundle response when applicable.
300 300 """
301 301
302 302 def __init__(self, repo, transactiongetter, captureoutput=True, source=''):
303 303 self.repo = repo
304 304 self.ui = repo.ui
305 305 self.records = unbundlerecords()
306 306 self.reply = None
307 307 self.captureoutput = captureoutput
308 308 self.hookargs = {}
309 309 self._gettransaction = transactiongetter
310 310 # carries value that can modify part behavior
311 311 self.modes = {}
312 312 self.source = source
313 313
314 314 def gettransaction(self):
315 315 transaction = self._gettransaction()
316 316
317 317 if self.hookargs:
318 318 # the ones added to the transaction supercede those added
319 319 # to the operation.
320 320 self.hookargs.update(transaction.hookargs)
321 321 transaction.hookargs = self.hookargs
322 322
323 323 # mark the hookargs as flushed. further attempts to add to
324 324 # hookargs will result in an abort.
325 325 self.hookargs = None
326 326
327 327 return transaction
328 328
329 329 def addhookargs(self, hookargs):
330 330 if self.hookargs is None:
331 331 raise error.ProgrammingError('attempted to add hookargs to '
332 332 'operation after transaction started')
333 333 self.hookargs.update(hookargs)
334 334
335 335 class TransactionUnavailable(RuntimeError):
336 336 pass
337 337
338 338 def _notransaction():
339 339 """default method to get a transaction while processing a bundle
340 340
341 341 Raise an exception to highlight the fact that no transaction was expected
342 342 to be created"""
343 343 raise TransactionUnavailable()
344 344
345 345 def applybundle(repo, unbundler, tr, source, url=None, **kwargs):
346 346 # transform me into unbundler.apply() as soon as the freeze is lifted
347 347 if isinstance(unbundler, unbundle20):
348 348 tr.hookargs['bundle2'] = '1'
349 349 if source is not None and 'source' not in tr.hookargs:
350 350 tr.hookargs['source'] = source
351 351 if url is not None and 'url' not in tr.hookargs:
352 352 tr.hookargs['url'] = url
353 353 return processbundle(repo, unbundler, lambda: tr, source=source)
354 354 else:
355 355 # the transactiongetter won't be used, but we might as well set it
356 356 op = bundleoperation(repo, lambda: tr, source=source)
357 357 _processchangegroup(op, unbundler, tr, source, url, **kwargs)
358 358 return op
359 359
360 360 class partiterator(object):
361 361 def __init__(self, repo, op, unbundler):
362 362 self.repo = repo
363 363 self.op = op
364 364 self.unbundler = unbundler
365 365 self.iterator = None
366 366 self.count = 0
367 367 self.current = None
368 368
369 369 def __enter__(self):
370 370 def func():
371 371 itr = enumerate(self.unbundler.iterparts())
372 372 for count, p in itr:
373 373 self.count = count
374 374 self.current = p
375 375 yield p
376 376 p.consume()
377 377 self.current = None
378 378 self.iterator = func()
379 379 return self.iterator
380 380
381 381 def __exit__(self, type, exc, tb):
382 382 if not self.iterator:
383 383 return
384 384
385 385 # Only gracefully abort in a normal exception situation. User aborts
386 386 # like Ctrl+C throw a KeyboardInterrupt which is not a base Exception,
387 387 # and should not gracefully cleanup.
388 388 if isinstance(exc, Exception):
389 389 # Any exceptions seeking to the end of the bundle at this point are
390 390 # almost certainly related to the underlying stream being bad.
391 391 # And, chances are that the exception we're handling is related to
392 392 # getting in that bad state. So, we swallow the seeking error and
393 393 # re-raise the original error.
394 394 seekerror = False
395 395 try:
396 396 if self.current:
397 397 # consume the part content to not corrupt the stream.
398 398 self.current.consume()
399 399
400 400 for part in self.iterator:
401 401 # consume the bundle content
402 402 part.consume()
403 403 except Exception:
404 404 seekerror = True
405 405
406 406 # Small hack to let caller code distinguish exceptions from bundle2
407 407 # processing from processing the old format. This is mostly needed
408 408 # to handle different return codes to unbundle according to the type
409 409 # of bundle. We should probably clean up or drop this return code
410 410 # craziness in a future version.
411 411 exc.duringunbundle2 = True
412 412 salvaged = []
413 413 replycaps = None
414 414 if self.op.reply is not None:
415 415 salvaged = self.op.reply.salvageoutput()
416 416 replycaps = self.op.reply.capabilities
417 417 exc._replycaps = replycaps
418 418 exc._bundle2salvagedoutput = salvaged
419 419
420 420 # Re-raising from a variable loses the original stack. So only use
421 421 # that form if we need to.
422 422 if seekerror:
423 423 raise exc
424 424
425 425 self.repo.ui.debug('bundle2-input-bundle: %i parts total\n' %
426 426 self.count)
427 427
428 428 def processbundle(repo, unbundler, transactiongetter=None, op=None, source=''):
429 429 """This function process a bundle, apply effect to/from a repo
430 430
431 431 It iterates over each part then searches for and uses the proper handling
432 432 code to process the part. Parts are processed in order.
433 433
434 434 Unknown Mandatory part will abort the process.
435 435
436 436 It is temporarily possible to provide a prebuilt bundleoperation to the
437 437 function. This is used to ensure output is properly propagated in case of
438 438 an error during the unbundling. This output capturing part will likely be
439 439 reworked and this ability will probably go away in the process.
440 440 """
441 441 if op is None:
442 442 if transactiongetter is None:
443 443 transactiongetter = _notransaction
444 444 op = bundleoperation(repo, transactiongetter, source=source)
445 445 # todo:
446 446 # - replace this is a init function soon.
447 447 # - exception catching
448 448 unbundler.params
449 449 if repo.ui.debugflag:
450 450 msg = ['bundle2-input-bundle:']
451 451 if unbundler.params:
452 452 msg.append(' %i params' % len(unbundler.params))
453 453 if op._gettransaction is None or op._gettransaction is _notransaction:
454 454 msg.append(' no-transaction')
455 455 else:
456 456 msg.append(' with-transaction')
457 457 msg.append('\n')
458 458 repo.ui.debug(''.join(msg))
459 459
460 460 processparts(repo, op, unbundler)
461 461
462 462 return op
463 463
464 464 def processparts(repo, op, unbundler):
465 465 with partiterator(repo, op, unbundler) as parts:
466 466 for part in parts:
467 467 _processpart(op, part)
468 468
469 469 def _processchangegroup(op, cg, tr, source, url, **kwargs):
470 470 ret = cg.apply(op.repo, tr, source, url, **kwargs)
471 471 op.records.add('changegroup', {
472 472 'return': ret,
473 473 })
474 474 return ret
475 475
476 476 def _gethandler(op, part):
477 477 status = 'unknown' # used by debug output
478 478 try:
479 479 handler = parthandlermapping.get(part.type)
480 480 if handler is None:
481 481 status = 'unsupported-type'
482 482 raise error.BundleUnknownFeatureError(parttype=part.type)
483 483 indebug(op.ui, 'found a handler for part %s' % part.type)
484 484 unknownparams = part.mandatorykeys - handler.params
485 485 if unknownparams:
486 486 unknownparams = list(unknownparams)
487 487 unknownparams.sort()
488 488 status = 'unsupported-params (%s)' % ', '.join(unknownparams)
489 489 raise error.BundleUnknownFeatureError(parttype=part.type,
490 490 params=unknownparams)
491 491 status = 'supported'
492 492 except error.BundleUnknownFeatureError as exc:
493 493 if part.mandatory: # mandatory parts
494 494 raise
495 495 indebug(op.ui, 'ignoring unsupported advisory part %s' % exc)
496 496 return # skip to part processing
497 497 finally:
498 498 if op.ui.debugflag:
499 499 msg = ['bundle2-input-part: "%s"' % part.type]
500 500 if not part.mandatory:
501 501 msg.append(' (advisory)')
502 502 nbmp = len(part.mandatorykeys)
503 503 nbap = len(part.params) - nbmp
504 504 if nbmp or nbap:
505 505 msg.append(' (params:')
506 506 if nbmp:
507 507 msg.append(' %i mandatory' % nbmp)
508 508 if nbap:
509 509 msg.append(' %i advisory' % nbmp)
510 510 msg.append(')')
511 511 msg.append(' %s\n' % status)
512 512 op.ui.debug(''.join(msg))
513 513
514 514 return handler
515 515
516 516 def _processpart(op, part):
517 517 """process a single part from a bundle
518 518
519 519 The part is guaranteed to have been fully consumed when the function exits
520 520 (even if an exception is raised)."""
521 521 handler = _gethandler(op, part)
522 522 if handler is None:
523 523 return
524 524
525 525 # handler is called outside the above try block so that we don't
526 526 # risk catching KeyErrors from anything other than the
527 527 # parthandlermapping lookup (any KeyError raised by handler()
528 528 # itself represents a defect of a different variety).
529 529 output = None
530 530 if op.captureoutput and op.reply is not None:
531 531 op.ui.pushbuffer(error=True, subproc=True)
532 532 output = ''
533 533 try:
534 534 handler(op, part)
535 535 finally:
536 536 if output is not None:
537 537 output = op.ui.popbuffer()
538 538 if output:
539 539 outpart = op.reply.newpart('output', data=output,
540 540 mandatory=False)
541 541 outpart.addparam(
542 542 'in-reply-to', pycompat.bytestr(part.id), mandatory=False)
543 543
544 544 def decodecaps(blob):
545 545 """decode a bundle2 caps bytes blob into a dictionary
546 546
547 547 The blob is a list of capabilities (one per line)
548 548 Capabilities may have values using a line of the form::
549 549
550 550 capability=value1,value2,value3
551 551
552 552 The values are always a list."""
553 553 caps = {}
554 554 for line in blob.splitlines():
555 555 if not line:
556 556 continue
557 557 if '=' not in line:
558 558 key, vals = line, ()
559 559 else:
560 560 key, vals = line.split('=', 1)
561 561 vals = vals.split(',')
562 562 key = urlreq.unquote(key)
563 563 vals = [urlreq.unquote(v) for v in vals]
564 564 caps[key] = vals
565 565 return caps
566 566
567 567 def encodecaps(caps):
568 568 """encode a bundle2 caps dictionary into a bytes blob"""
569 569 chunks = []
570 570 for ca in sorted(caps):
571 571 vals = caps[ca]
572 572 ca = urlreq.quote(ca)
573 573 vals = [urlreq.quote(v) for v in vals]
574 574 if vals:
575 575 ca = "%s=%s" % (ca, ','.join(vals))
576 576 chunks.append(ca)
577 577 return '\n'.join(chunks)
578 578
579 579 bundletypes = {
580 580 "": ("", 'UN'), # only when using unbundle on ssh and old http servers
581 581 # since the unification ssh accepts a header but there
582 582 # is no capability signaling it.
583 583 "HG20": (), # special-cased below
584 584 "HG10UN": ("HG10UN", 'UN'),
585 585 "HG10BZ": ("HG10", 'BZ'),
586 586 "HG10GZ": ("HG10GZ", 'GZ'),
587 587 }
588 588
589 589 # hgweb uses this list to communicate its preferred type
590 590 bundlepriority = ['HG10GZ', 'HG10BZ', 'HG10UN']
591 591
592 592 class bundle20(object):
593 593 """represent an outgoing bundle2 container
594 594
595 595 Use the `addparam` method to add stream level parameter. and `newpart` to
596 596 populate it. Then call `getchunks` to retrieve all the binary chunks of
597 597 data that compose the bundle2 container."""
598 598
599 599 _magicstring = 'HG20'
600 600
601 601 def __init__(self, ui, capabilities=()):
602 602 self.ui = ui
603 603 self._params = []
604 604 self._parts = []
605 605 self.capabilities = dict(capabilities)
606 606 self._compengine = util.compengines.forbundletype('UN')
607 607 self._compopts = None
608 608 # If compression is being handled by a consumer of the raw
609 609 # data (e.g. the wire protocol), unsetting this flag tells
610 610 # consumers that the bundle is best left uncompressed.
611 611 self.prefercompressed = True
612 612
613 613 def setcompression(self, alg, compopts=None):
614 614 """setup core part compression to <alg>"""
615 615 if alg in (None, 'UN'):
616 616 return
617 617 assert not any(n.lower() == 'compression' for n, v in self._params)
618 618 self.addparam('Compression', alg)
619 619 self._compengine = util.compengines.forbundletype(alg)
620 620 self._compopts = compopts
621 621
622 622 @property
623 623 def nbparts(self):
624 624 """total number of parts added to the bundler"""
625 625 return len(self._parts)
626 626
627 627 # methods used to defines the bundle2 content
628 628 def addparam(self, name, value=None):
629 629 """add a stream level parameter"""
630 630 if not name:
631 631 raise error.ProgrammingError(b'empty parameter name')
632 632 if name[0:1] not in pycompat.bytestr(string.ascii_letters):
633 633 raise error.ProgrammingError(b'non letter first character: %s'
634 634 % name)
635 635 self._params.append((name, value))
636 636
637 637 def addpart(self, part):
638 638 """add a new part to the bundle2 container
639 639
640 640 Parts contains the actual applicative payload."""
641 641 assert part.id is None
642 642 part.id = len(self._parts) # very cheap counter
643 643 self._parts.append(part)
644 644
645 645 def newpart(self, typeid, *args, **kwargs):
646 646 """create a new part and add it to the containers
647 647
648 648 As the part is directly added to the containers. For now, this means
649 649 that any failure to properly initialize the part after calling
650 650 ``newpart`` should result in a failure of the whole bundling process.
651 651
652 652 You can still fall back to manually create and add if you need better
653 653 control."""
654 654 part = bundlepart(typeid, *args, **kwargs)
655 655 self.addpart(part)
656 656 return part
657 657
658 658 # methods used to generate the bundle2 stream
659 659 def getchunks(self):
660 660 if self.ui.debugflag:
661 661 msg = ['bundle2-output-bundle: "%s",' % self._magicstring]
662 662 if self._params:
663 663 msg.append(' (%i params)' % len(self._params))
664 664 msg.append(' %i parts total\n' % len(self._parts))
665 665 self.ui.debug(''.join(msg))
666 666 outdebug(self.ui, 'start emission of %s stream' % self._magicstring)
667 667 yield self._magicstring
668 668 param = self._paramchunk()
669 669 outdebug(self.ui, 'bundle parameter: %s' % param)
670 670 yield _pack(_fstreamparamsize, len(param))
671 671 if param:
672 672 yield param
673 673 for chunk in self._compengine.compressstream(self._getcorechunk(),
674 674 self._compopts):
675 675 yield chunk
676 676
677 677 def _paramchunk(self):
678 678 """return a encoded version of all stream parameters"""
679 679 blocks = []
680 680 for par, value in self._params:
681 681 par = urlreq.quote(par)
682 682 if value is not None:
683 683 value = urlreq.quote(value)
684 684 par = '%s=%s' % (par, value)
685 685 blocks.append(par)
686 686 return ' '.join(blocks)
687 687
688 688 def _getcorechunk(self):
689 689 """yield chunk for the core part of the bundle
690 690
691 691 (all but headers and parameters)"""
692 692 outdebug(self.ui, 'start of parts')
693 693 for part in self._parts:
694 694 outdebug(self.ui, 'bundle part: "%s"' % part.type)
695 695 for chunk in part.getchunks(ui=self.ui):
696 696 yield chunk
697 697 outdebug(self.ui, 'end of bundle')
698 698 yield _pack(_fpartheadersize, 0)
699 699
700 700
701 701 def salvageoutput(self):
702 702 """return a list with a copy of all output parts in the bundle
703 703
704 704 This is meant to be used during error handling to make sure we preserve
705 705 server output"""
706 706 salvaged = []
707 707 for part in self._parts:
708 708 if part.type.startswith('output'):
709 709 salvaged.append(part.copy())
710 710 return salvaged
711 711
712 712
713 713 class unpackermixin(object):
714 714 """A mixin to extract bytes and struct data from a stream"""
715 715
716 716 def __init__(self, fp):
717 717 self._fp = fp
718 718
719 719 def _unpack(self, format):
720 720 """unpack this struct format from the stream
721 721
722 722 This method is meant for internal usage by the bundle2 protocol only.
723 723 They directly manipulate the low level stream including bundle2 level
724 724 instruction.
725 725
726 726 Do not use it to implement higher-level logic or methods."""
727 727 data = self._readexact(struct.calcsize(format))
728 728 return _unpack(format, data)
729 729
730 730 def _readexact(self, size):
731 731 """read exactly <size> bytes from the stream
732 732
733 733 This method is meant for internal usage by the bundle2 protocol only.
734 734 They directly manipulate the low level stream including bundle2 level
735 735 instruction.
736 736
737 737 Do not use it to implement higher-level logic or methods."""
738 738 return changegroup.readexactly(self._fp, size)
739 739
740 740 def getunbundler(ui, fp, magicstring=None):
741 741 """return a valid unbundler object for a given magicstring"""
742 742 if magicstring is None:
743 743 magicstring = changegroup.readexactly(fp, 4)
744 744 magic, version = magicstring[0:2], magicstring[2:4]
745 745 if magic != 'HG':
746 746 ui.debug(
747 747 "error: invalid magic: %r (version %r), should be 'HG'\n"
748 748 % (magic, version))
749 749 raise error.Abort(_('not a Mercurial bundle'))
750 750 unbundlerclass = formatmap.get(version)
751 751 if unbundlerclass is None:
752 752 raise error.Abort(_('unknown bundle version %s') % version)
753 753 unbundler = unbundlerclass(ui, fp)
754 754 indebug(ui, 'start processing of %s stream' % magicstring)
755 755 return unbundler
756 756
757 757 class unbundle20(unpackermixin):
758 758 """interpret a bundle2 stream
759 759
760 760 This class is fed with a binary stream and yields parts through its
761 761 `iterparts` methods."""
762 762
763 763 _magicstring = 'HG20'
764 764
765 765 def __init__(self, ui, fp):
766 766 """If header is specified, we do not read it out of the stream."""
767 767 self.ui = ui
768 768 self._compengine = util.compengines.forbundletype('UN')
769 769 self._compressed = None
770 770 super(unbundle20, self).__init__(fp)
771 771
772 772 @util.propertycache
773 773 def params(self):
774 774 """dictionary of stream level parameters"""
775 775 indebug(self.ui, 'reading bundle2 stream parameters')
776 776 params = {}
777 777 paramssize = self._unpack(_fstreamparamsize)[0]
778 778 if paramssize < 0:
779 779 raise error.BundleValueError('negative bundle param size: %i'
780 780 % paramssize)
781 781 if paramssize:
782 782 params = self._readexact(paramssize)
783 783 params = self._processallparams(params)
784 784 return params
785 785
786 786 def _processallparams(self, paramsblock):
787 787 """"""
788 788 params = util.sortdict()
789 789 for p in paramsblock.split(' '):
790 790 p = p.split('=', 1)
791 791 p = [urlreq.unquote(i) for i in p]
792 792 if len(p) < 2:
793 793 p.append(None)
794 794 self._processparam(*p)
795 795 params[p[0]] = p[1]
796 796 return params
797 797
798 798
799 799 def _processparam(self, name, value):
800 800 """process a parameter, applying its effect if needed
801 801
802 802 Parameter starting with a lower case letter are advisory and will be
803 803 ignored when unknown. Those starting with an upper case letter are
804 804 mandatory and will this function will raise a KeyError when unknown.
805 805
806 806 Note: no option are currently supported. Any input will be either
807 807 ignored or failing.
808 808 """
809 809 if not name:
810 810 raise ValueError(r'empty parameter name')
811 811 if name[0:1] not in pycompat.bytestr(string.ascii_letters):
812 812 raise ValueError(r'non letter first character: %s' % name)
813 813 try:
814 814 handler = b2streamparamsmap[name.lower()]
815 815 except KeyError:
816 816 if name[0:1].islower():
817 817 indebug(self.ui, "ignoring unknown parameter %s" % name)
818 818 else:
819 819 raise error.BundleUnknownFeatureError(params=(name,))
820 820 else:
821 821 handler(self, name, value)
822 822
823 823 def _forwardchunks(self):
824 824 """utility to transfer a bundle2 as binary
825 825
826 826 This is made necessary by the fact the 'getbundle' command over 'ssh'
827 827 have no way to know then the reply end, relying on the bundle to be
828 828 interpreted to know its end. This is terrible and we are sorry, but we
829 829 needed to move forward to get general delta enabled.
830 830 """
831 831 yield self._magicstring
832 832 assert 'params' not in vars(self)
833 833 paramssize = self._unpack(_fstreamparamsize)[0]
834 834 if paramssize < 0:
835 835 raise error.BundleValueError('negative bundle param size: %i'
836 836 % paramssize)
837 837 yield _pack(_fstreamparamsize, paramssize)
838 838 if paramssize:
839 839 params = self._readexact(paramssize)
840 840 self._processallparams(params)
841 841 yield params
842 842 assert self._compengine.bundletype == 'UN'
843 843 # From there, payload might need to be decompressed
844 844 self._fp = self._compengine.decompressorreader(self._fp)
845 845 emptycount = 0
846 846 while emptycount < 2:
847 847 # so we can brainlessly loop
848 848 assert _fpartheadersize == _fpayloadsize
849 849 size = self._unpack(_fpartheadersize)[0]
850 850 yield _pack(_fpartheadersize, size)
851 851 if size:
852 852 emptycount = 0
853 853 else:
854 854 emptycount += 1
855 855 continue
856 856 if size == flaginterrupt:
857 857 continue
858 858 elif size < 0:
859 859 raise error.BundleValueError('negative chunk size: %i')
860 860 yield self._readexact(size)
861 861
862 862
863 863 def iterparts(self, seekable=False):
864 864 """yield all parts contained in the stream"""
865 865 cls = seekableunbundlepart if seekable else unbundlepart
866 866 # make sure param have been loaded
867 867 self.params
868 868 # From there, payload need to be decompressed
869 869 self._fp = self._compengine.decompressorreader(self._fp)
870 870 indebug(self.ui, 'start extraction of bundle2 parts')
871 871 headerblock = self._readpartheader()
872 872 while headerblock is not None:
873 873 part = cls(self.ui, headerblock, self._fp)
874 874 yield part
875 875 # Ensure part is fully consumed so we can start reading the next
876 876 # part.
877 877 part.consume()
878 878
879 879 headerblock = self._readpartheader()
880 880 indebug(self.ui, 'end of bundle2 stream')
881 881
882 882 def _readpartheader(self):
883 883 """reads a part header size and return the bytes blob
884 884
885 885 returns None if empty"""
886 886 headersize = self._unpack(_fpartheadersize)[0]
887 887 if headersize < 0:
888 888 raise error.BundleValueError('negative part header size: %i'
889 889 % headersize)
890 890 indebug(self.ui, 'part header size: %i' % headersize)
891 891 if headersize:
892 892 return self._readexact(headersize)
893 893 return None
894 894
895 895 def compressed(self):
896 896 self.params # load params
897 897 return self._compressed
898 898
899 899 def close(self):
900 900 """close underlying file"""
901 901 if util.safehasattr(self._fp, 'close'):
902 902 return self._fp.close()
903 903
904 904 formatmap = {'20': unbundle20}
905 905
906 906 b2streamparamsmap = {}
907 907
908 908 def b2streamparamhandler(name):
909 909 """register a handler for a stream level parameter"""
910 910 def decorator(func):
911 911 assert name not in formatmap
912 912 b2streamparamsmap[name] = func
913 913 return func
914 914 return decorator
915 915
916 916 @b2streamparamhandler('compression')
917 917 def processcompression(unbundler, param, value):
918 918 """read compression parameter and install payload decompression"""
919 919 if value not in util.compengines.supportedbundletypes:
920 920 raise error.BundleUnknownFeatureError(params=(param,),
921 921 values=(value,))
922 922 unbundler._compengine = util.compengines.forbundletype(value)
923 923 if value is not None:
924 924 unbundler._compressed = True
925 925
926 926 class bundlepart(object):
927 927 """A bundle2 part contains application level payload
928 928
929 929 The part `type` is used to route the part to the application level
930 930 handler.
931 931
932 932 The part payload is contained in ``part.data``. It could be raw bytes or a
933 933 generator of byte chunks.
934 934
935 935 You can add parameters to the part using the ``addparam`` method.
936 936 Parameters can be either mandatory (default) or advisory. Remote side
937 937 should be able to safely ignore the advisory ones.
938 938
939 939 Both data and parameters cannot be modified after the generation has begun.
940 940 """
941 941
942 942 def __init__(self, parttype, mandatoryparams=(), advisoryparams=(),
943 943 data='', mandatory=True):
944 944 validateparttype(parttype)
945 945 self.id = None
946 946 self.type = parttype
947 947 self._data = data
948 948 self._mandatoryparams = list(mandatoryparams)
949 949 self._advisoryparams = list(advisoryparams)
950 950 # checking for duplicated entries
951 951 self._seenparams = set()
952 952 for pname, __ in self._mandatoryparams + self._advisoryparams:
953 953 if pname in self._seenparams:
954 954 raise error.ProgrammingError('duplicated params: %s' % pname)
955 955 self._seenparams.add(pname)
956 956 # status of the part's generation:
957 957 # - None: not started,
958 958 # - False: currently generated,
959 959 # - True: generation done.
960 960 self._generated = None
961 961 self.mandatory = mandatory
962 962
963 963 def __repr__(self):
964 964 cls = "%s.%s" % (self.__class__.__module__, self.__class__.__name__)
965 965 return ('<%s object at %x; id: %s; type: %s; mandatory: %s>'
966 966 % (cls, id(self), self.id, self.type, self.mandatory))
967 967
968 968 def copy(self):
969 969 """return a copy of the part
970 970
971 971 The new part have the very same content but no partid assigned yet.
972 972 Parts with generated data cannot be copied."""
973 973 assert not util.safehasattr(self.data, 'next')
974 974 return self.__class__(self.type, self._mandatoryparams,
975 975 self._advisoryparams, self._data, self.mandatory)
976 976
977 977 # methods used to defines the part content
978 978 @property
979 979 def data(self):
980 980 return self._data
981 981
982 982 @data.setter
983 983 def data(self, data):
984 984 if self._generated is not None:
985 985 raise error.ReadOnlyPartError('part is being generated')
986 986 self._data = data
987 987
988 988 @property
989 989 def mandatoryparams(self):
990 990 # make it an immutable tuple to force people through ``addparam``
991 991 return tuple(self._mandatoryparams)
992 992
993 993 @property
994 994 def advisoryparams(self):
995 995 # make it an immutable tuple to force people through ``addparam``
996 996 return tuple(self._advisoryparams)
997 997
998 998 def addparam(self, name, value='', mandatory=True):
999 999 """add a parameter to the part
1000 1000
1001 1001 If 'mandatory' is set to True, the remote handler must claim support
1002 1002 for this parameter or the unbundling will be aborted.
1003 1003
1004 1004 The 'name' and 'value' cannot exceed 255 bytes each.
1005 1005 """
1006 1006 if self._generated is not None:
1007 1007 raise error.ReadOnlyPartError('part is being generated')
1008 1008 if name in self._seenparams:
1009 1009 raise ValueError('duplicated params: %s' % name)
1010 1010 self._seenparams.add(name)
1011 1011 params = self._advisoryparams
1012 1012 if mandatory:
1013 1013 params = self._mandatoryparams
1014 1014 params.append((name, value))
1015 1015
1016 1016 # methods used to generates the bundle2 stream
1017 1017 def getchunks(self, ui):
1018 1018 if self._generated is not None:
1019 1019 raise error.ProgrammingError('part can only be consumed once')
1020 1020 self._generated = False
1021 1021
1022 1022 if ui.debugflag:
1023 1023 msg = ['bundle2-output-part: "%s"' % self.type]
1024 1024 if not self.mandatory:
1025 1025 msg.append(' (advisory)')
1026 1026 nbmp = len(self.mandatoryparams)
1027 1027 nbap = len(self.advisoryparams)
1028 1028 if nbmp or nbap:
1029 1029 msg.append(' (params:')
1030 1030 if nbmp:
1031 1031 msg.append(' %i mandatory' % nbmp)
1032 1032 if nbap:
1033 1033 msg.append(' %i advisory' % nbmp)
1034 1034 msg.append(')')
1035 1035 if not self.data:
1036 1036 msg.append(' empty payload')
1037 1037 elif (util.safehasattr(self.data, 'next')
1038 1038 or util.safehasattr(self.data, '__next__')):
1039 1039 msg.append(' streamed payload')
1040 1040 else:
1041 1041 msg.append(' %i bytes payload' % len(self.data))
1042 1042 msg.append('\n')
1043 1043 ui.debug(''.join(msg))
1044 1044
1045 1045 #### header
1046 1046 if self.mandatory:
1047 1047 parttype = self.type.upper()
1048 1048 else:
1049 1049 parttype = self.type.lower()
1050 1050 outdebug(ui, 'part %s: "%s"' % (pycompat.bytestr(self.id), parttype))
1051 1051 ## parttype
1052 1052 header = [_pack(_fparttypesize, len(parttype)),
1053 1053 parttype, _pack(_fpartid, self.id),
1054 1054 ]
1055 1055 ## parameters
1056 1056 # count
1057 1057 manpar = self.mandatoryparams
1058 1058 advpar = self.advisoryparams
1059 1059 header.append(_pack(_fpartparamcount, len(manpar), len(advpar)))
1060 1060 # size
1061 1061 parsizes = []
1062 1062 for key, value in manpar:
1063 1063 parsizes.append(len(key))
1064 1064 parsizes.append(len(value))
1065 1065 for key, value in advpar:
1066 1066 parsizes.append(len(key))
1067 1067 parsizes.append(len(value))
1068 1068 paramsizes = _pack(_makefpartparamsizes(len(parsizes) // 2), *parsizes)
1069 1069 header.append(paramsizes)
1070 1070 # key, value
1071 1071 for key, value in manpar:
1072 1072 header.append(key)
1073 1073 header.append(value)
1074 1074 for key, value in advpar:
1075 1075 header.append(key)
1076 1076 header.append(value)
1077 1077 ## finalize header
1078 1078 try:
1079 1079 headerchunk = ''.join(header)
1080 1080 except TypeError:
1081 1081 raise TypeError(r'Found a non-bytes trying to '
1082 1082 r'build bundle part header: %r' % header)
1083 1083 outdebug(ui, 'header chunk size: %i' % len(headerchunk))
1084 1084 yield _pack(_fpartheadersize, len(headerchunk))
1085 1085 yield headerchunk
1086 1086 ## payload
1087 1087 try:
1088 1088 for chunk in self._payloadchunks():
1089 1089 outdebug(ui, 'payload chunk size: %i' % len(chunk))
1090 1090 yield _pack(_fpayloadsize, len(chunk))
1091 1091 yield chunk
1092 1092 except GeneratorExit:
1093 1093 # GeneratorExit means that nobody is listening for our
1094 1094 # results anyway, so just bail quickly rather than trying
1095 1095 # to produce an error part.
1096 1096 ui.debug('bundle2-generatorexit\n')
1097 1097 raise
1098 1098 except BaseException as exc:
1099 1099 bexc = stringutil.forcebytestr(exc)
1100 1100 # backup exception data for later
1101 1101 ui.debug('bundle2-input-stream-interrupt: encoding exception %s'
1102 1102 % bexc)
1103 1103 tb = sys.exc_info()[2]
1104 1104 msg = 'unexpected error: %s' % bexc
1105 1105 interpart = bundlepart('error:abort', [('message', msg)],
1106 1106 mandatory=False)
1107 1107 interpart.id = 0
1108 1108 yield _pack(_fpayloadsize, -1)
1109 1109 for chunk in interpart.getchunks(ui=ui):
1110 1110 yield chunk
1111 1111 outdebug(ui, 'closing payload chunk')
1112 1112 # abort current part payload
1113 1113 yield _pack(_fpayloadsize, 0)
1114 1114 pycompat.raisewithtb(exc, tb)
1115 1115 # end of payload
1116 1116 outdebug(ui, 'closing payload chunk')
1117 1117 yield _pack(_fpayloadsize, 0)
1118 1118 self._generated = True
1119 1119
1120 1120 def _payloadchunks(self):
1121 1121 """yield chunks of a the part payload
1122 1122
1123 1123 Exists to handle the different methods to provide data to a part."""
1124 1124 # we only support fixed size data now.
1125 1125 # This will be improved in the future.
1126 1126 if (util.safehasattr(self.data, 'next')
1127 1127 or util.safehasattr(self.data, '__next__')):
1128 1128 buff = util.chunkbuffer(self.data)
1129 1129 chunk = buff.read(preferedchunksize)
1130 1130 while chunk:
1131 1131 yield chunk
1132 1132 chunk = buff.read(preferedchunksize)
1133 1133 elif len(self.data):
1134 1134 yield self.data
1135 1135
1136 1136
1137 1137 flaginterrupt = -1
1138 1138
1139 1139 class interrupthandler(unpackermixin):
1140 1140 """read one part and process it with restricted capability
1141 1141
1142 1142 This allows to transmit exception raised on the producer size during part
1143 1143 iteration while the consumer is reading a part.
1144 1144
1145 1145 Part processed in this manner only have access to a ui object,"""
1146 1146
1147 1147 def __init__(self, ui, fp):
1148 1148 super(interrupthandler, self).__init__(fp)
1149 1149 self.ui = ui
1150 1150
1151 1151 def _readpartheader(self):
1152 1152 """reads a part header size and return the bytes blob
1153 1153
1154 1154 returns None if empty"""
1155 1155 headersize = self._unpack(_fpartheadersize)[0]
1156 1156 if headersize < 0:
1157 1157 raise error.BundleValueError('negative part header size: %i'
1158 1158 % headersize)
1159 1159 indebug(self.ui, 'part header size: %i\n' % headersize)
1160 1160 if headersize:
1161 1161 return self._readexact(headersize)
1162 1162 return None
1163 1163
1164 1164 def __call__(self):
1165 1165
1166 1166 self.ui.debug('bundle2-input-stream-interrupt:'
1167 1167 ' opening out of band context\n')
1168 1168 indebug(self.ui, 'bundle2 stream interruption, looking for a part.')
1169 1169 headerblock = self._readpartheader()
1170 1170 if headerblock is None:
1171 1171 indebug(self.ui, 'no part found during interruption.')
1172 1172 return
1173 1173 part = unbundlepart(self.ui, headerblock, self._fp)
1174 1174 op = interruptoperation(self.ui)
1175 1175 hardabort = False
1176 1176 try:
1177 1177 _processpart(op, part)
1178 1178 except (SystemExit, KeyboardInterrupt):
1179 1179 hardabort = True
1180 1180 raise
1181 1181 finally:
1182 1182 if not hardabort:
1183 1183 part.consume()
1184 1184 self.ui.debug('bundle2-input-stream-interrupt:'
1185 1185 ' closing out of band context\n')
1186 1186
1187 1187 class interruptoperation(object):
1188 1188 """A limited operation to be use by part handler during interruption
1189 1189
1190 1190 It only have access to an ui object.
1191 1191 """
1192 1192
1193 1193 def __init__(self, ui):
1194 1194 self.ui = ui
1195 1195 self.reply = None
1196 1196 self.captureoutput = False
1197 1197
1198 1198 @property
1199 1199 def repo(self):
1200 1200 raise error.ProgrammingError('no repo access from stream interruption')
1201 1201
1202 1202 def gettransaction(self):
1203 1203 raise TransactionUnavailable('no repo access from stream interruption')
1204 1204
1205 1205 def decodepayloadchunks(ui, fh):
1206 1206 """Reads bundle2 part payload data into chunks.
1207 1207
1208 1208 Part payload data consists of framed chunks. This function takes
1209 1209 a file handle and emits those chunks.
1210 1210 """
1211 1211 dolog = ui.configbool('devel', 'bundle2.debug')
1212 1212 debug = ui.debug
1213 1213
1214 1214 headerstruct = struct.Struct(_fpayloadsize)
1215 1215 headersize = headerstruct.size
1216 1216 unpack = headerstruct.unpack
1217 1217
1218 1218 readexactly = changegroup.readexactly
1219 1219 read = fh.read
1220 1220
1221 1221 chunksize = unpack(readexactly(fh, headersize))[0]
1222 1222 indebug(ui, 'payload chunk size: %i' % chunksize)
1223 1223
1224 1224 # changegroup.readexactly() is inlined below for performance.
1225 1225 while chunksize:
1226 1226 if chunksize >= 0:
1227 1227 s = read(chunksize)
1228 1228 if len(s) < chunksize:
1229 1229 raise error.Abort(_('stream ended unexpectedly '
1230 1230 ' (got %d bytes, expected %d)') %
1231 1231 (len(s), chunksize))
1232 1232
1233 1233 yield s
1234 1234 elif chunksize == flaginterrupt:
1235 1235 # Interrupt "signal" detected. The regular stream is interrupted
1236 1236 # and a bundle2 part follows. Consume it.
1237 1237 interrupthandler(ui, fh)()
1238 1238 else:
1239 1239 raise error.BundleValueError(
1240 1240 'negative payload chunk size: %s' % chunksize)
1241 1241
1242 1242 s = read(headersize)
1243 1243 if len(s) < headersize:
1244 1244 raise error.Abort(_('stream ended unexpectedly '
1245 1245 ' (got %d bytes, expected %d)') %
1246 1246 (len(s), chunksize))
1247 1247
1248 1248 chunksize = unpack(s)[0]
1249 1249
1250 1250 # indebug() inlined for performance.
1251 1251 if dolog:
1252 1252 debug('bundle2-input: payload chunk size: %i\n' % chunksize)
1253 1253
1254 1254 class unbundlepart(unpackermixin):
1255 1255 """a bundle part read from a bundle"""
1256 1256
1257 1257 def __init__(self, ui, header, fp):
1258 1258 super(unbundlepart, self).__init__(fp)
1259 1259 self._seekable = (util.safehasattr(fp, 'seek') and
1260 1260 util.safehasattr(fp, 'tell'))
1261 1261 self.ui = ui
1262 1262 # unbundle state attr
1263 1263 self._headerdata = header
1264 1264 self._headeroffset = 0
1265 1265 self._initialized = False
1266 1266 self.consumed = False
1267 1267 # part data
1268 1268 self.id = None
1269 1269 self.type = None
1270 1270 self.mandatoryparams = None
1271 1271 self.advisoryparams = None
1272 1272 self.params = None
1273 1273 self.mandatorykeys = ()
1274 1274 self._readheader()
1275 1275 self._mandatory = None
1276 1276 self._pos = 0
1277 1277
1278 1278 def _fromheader(self, size):
1279 1279 """return the next <size> byte from the header"""
1280 1280 offset = self._headeroffset
1281 1281 data = self._headerdata[offset:(offset + size)]
1282 1282 self._headeroffset = offset + size
1283 1283 return data
1284 1284
1285 1285 def _unpackheader(self, format):
1286 1286 """read given format from header
1287 1287
1288 1288 This automatically compute the size of the format to read."""
1289 1289 data = self._fromheader(struct.calcsize(format))
1290 1290 return _unpack(format, data)
1291 1291
1292 1292 def _initparams(self, mandatoryparams, advisoryparams):
1293 1293 """internal function to setup all logic related parameters"""
1294 1294 # make it read only to prevent people touching it by mistake.
1295 1295 self.mandatoryparams = tuple(mandatoryparams)
1296 1296 self.advisoryparams = tuple(advisoryparams)
1297 1297 # user friendly UI
1298 1298 self.params = util.sortdict(self.mandatoryparams)
1299 1299 self.params.update(self.advisoryparams)
1300 1300 self.mandatorykeys = frozenset(p[0] for p in mandatoryparams)
1301 1301
1302 1302 def _readheader(self):
1303 1303 """read the header and setup the object"""
1304 1304 typesize = self._unpackheader(_fparttypesize)[0]
1305 1305 self.type = self._fromheader(typesize)
1306 1306 indebug(self.ui, 'part type: "%s"' % self.type)
1307 1307 self.id = self._unpackheader(_fpartid)[0]
1308 1308 indebug(self.ui, 'part id: "%s"' % pycompat.bytestr(self.id))
1309 1309 # extract mandatory bit from type
1310 1310 self.mandatory = (self.type != self.type.lower())
1311 1311 self.type = self.type.lower()
1312 1312 ## reading parameters
1313 1313 # param count
1314 1314 mancount, advcount = self._unpackheader(_fpartparamcount)
1315 1315 indebug(self.ui, 'part parameters: %i' % (mancount + advcount))
1316 1316 # param size
1317 1317 fparamsizes = _makefpartparamsizes(mancount + advcount)
1318 1318 paramsizes = self._unpackheader(fparamsizes)
1319 1319 # make it a list of couple again
1320 1320 paramsizes = list(zip(paramsizes[::2], paramsizes[1::2]))
1321 1321 # split mandatory from advisory
1322 1322 mansizes = paramsizes[:mancount]
1323 1323 advsizes = paramsizes[mancount:]
1324 1324 # retrieve param value
1325 1325 manparams = []
1326 1326 for key, value in mansizes:
1327 1327 manparams.append((self._fromheader(key), self._fromheader(value)))
1328 1328 advparams = []
1329 1329 for key, value in advsizes:
1330 1330 advparams.append((self._fromheader(key), self._fromheader(value)))
1331 1331 self._initparams(manparams, advparams)
1332 1332 ## part payload
1333 1333 self._payloadstream = util.chunkbuffer(self._payloadchunks())
1334 1334 # we read the data, tell it
1335 1335 self._initialized = True
1336 1336
1337 1337 def _payloadchunks(self):
1338 1338 """Generator of decoded chunks in the payload."""
1339 1339 return decodepayloadchunks(self.ui, self._fp)
1340 1340
1341 1341 def consume(self):
1342 1342 """Read the part payload until completion.
1343 1343
1344 1344 By consuming the part data, the underlying stream read offset will
1345 1345 be advanced to the next part (or end of stream).
1346 1346 """
1347 1347 if self.consumed:
1348 1348 return
1349 1349
1350 1350 chunk = self.read(32768)
1351 1351 while chunk:
1352 1352 self._pos += len(chunk)
1353 1353 chunk = self.read(32768)
1354 1354
1355 1355 def read(self, size=None):
1356 1356 """read payload data"""
1357 1357 if not self._initialized:
1358 1358 self._readheader()
1359 1359 if size is None:
1360 1360 data = self._payloadstream.read()
1361 1361 else:
1362 1362 data = self._payloadstream.read(size)
1363 1363 self._pos += len(data)
1364 1364 if size is None or len(data) < size:
1365 1365 if not self.consumed and self._pos:
1366 1366 self.ui.debug('bundle2-input-part: total payload size %i\n'
1367 1367 % self._pos)
1368 1368 self.consumed = True
1369 1369 return data
1370 1370
1371 1371 class seekableunbundlepart(unbundlepart):
1372 1372 """A bundle2 part in a bundle that is seekable.
1373 1373
1374 1374 Regular ``unbundlepart`` instances can only be read once. This class
1375 1375 extends ``unbundlepart`` to enable bi-directional seeking within the
1376 1376 part.
1377 1377
1378 1378 Bundle2 part data consists of framed chunks. Offsets when seeking
1379 1379 refer to the decoded data, not the offsets in the underlying bundle2
1380 1380 stream.
1381 1381
1382 1382 To facilitate quickly seeking within the decoded data, instances of this
1383 1383 class maintain a mapping between offsets in the underlying stream and
1384 1384 the decoded payload. This mapping will consume memory in proportion
1385 1385 to the number of chunks within the payload (which almost certainly
1386 1386 increases in proportion with the size of the part).
1387 1387 """
1388 1388 def __init__(self, ui, header, fp):
1389 1389 # (payload, file) offsets for chunk starts.
1390 1390 self._chunkindex = []
1391 1391
1392 1392 super(seekableunbundlepart, self).__init__(ui, header, fp)
1393 1393
1394 1394 def _payloadchunks(self, chunknum=0):
1395 1395 '''seek to specified chunk and start yielding data'''
1396 1396 if len(self._chunkindex) == 0:
1397 1397 assert chunknum == 0, 'Must start with chunk 0'
1398 1398 self._chunkindex.append((0, self._tellfp()))
1399 1399 else:
1400 1400 assert chunknum < len(self._chunkindex), \
1401 1401 'Unknown chunk %d' % chunknum
1402 1402 self._seekfp(self._chunkindex[chunknum][1])
1403 1403
1404 1404 pos = self._chunkindex[chunknum][0]
1405 1405
1406 1406 for chunk in decodepayloadchunks(self.ui, self._fp):
1407 1407 chunknum += 1
1408 1408 pos += len(chunk)
1409 1409 if chunknum == len(self._chunkindex):
1410 1410 self._chunkindex.append((pos, self._tellfp()))
1411 1411
1412 1412 yield chunk
1413 1413
1414 1414 def _findchunk(self, pos):
1415 1415 '''for a given payload position, return a chunk number and offset'''
1416 1416 for chunk, (ppos, fpos) in enumerate(self._chunkindex):
1417 1417 if ppos == pos:
1418 1418 return chunk, 0
1419 1419 elif ppos > pos:
1420 1420 return chunk - 1, pos - self._chunkindex[chunk - 1][0]
1421 1421 raise ValueError('Unknown chunk')
1422 1422
1423 1423 def tell(self):
1424 1424 return self._pos
1425 1425
1426 1426 def seek(self, offset, whence=os.SEEK_SET):
1427 1427 if whence == os.SEEK_SET:
1428 1428 newpos = offset
1429 1429 elif whence == os.SEEK_CUR:
1430 1430 newpos = self._pos + offset
1431 1431 elif whence == os.SEEK_END:
1432 1432 if not self.consumed:
1433 1433 # Can't use self.consume() here because it advances self._pos.
1434 1434 chunk = self.read(32768)
1435 1435 while chunk:
1436 1436 chunk = self.read(32768)
1437 1437 newpos = self._chunkindex[-1][0] - offset
1438 1438 else:
1439 1439 raise ValueError('Unknown whence value: %r' % (whence,))
1440 1440
1441 1441 if newpos > self._chunkindex[-1][0] and not self.consumed:
1442 1442 # Can't use self.consume() here because it advances self._pos.
1443 1443 chunk = self.read(32768)
1444 1444 while chunk:
1445 1445 chunk = self.read(32668)
1446 1446
1447 1447 if not 0 <= newpos <= self._chunkindex[-1][0]:
1448 1448 raise ValueError('Offset out of range')
1449 1449
1450 1450 if self._pos != newpos:
1451 1451 chunk, internaloffset = self._findchunk(newpos)
1452 1452 self._payloadstream = util.chunkbuffer(self._payloadchunks(chunk))
1453 1453 adjust = self.read(internaloffset)
1454 1454 if len(adjust) != internaloffset:
1455 1455 raise error.Abort(_('Seek failed\n'))
1456 1456 self._pos = newpos
1457 1457
1458 1458 def _seekfp(self, offset, whence=0):
1459 1459 """move the underlying file pointer
1460 1460
1461 1461 This method is meant for internal usage by the bundle2 protocol only.
1462 1462 They directly manipulate the low level stream including bundle2 level
1463 1463 instruction.
1464 1464
1465 1465 Do not use it to implement higher-level logic or methods."""
1466 1466 if self._seekable:
1467 1467 return self._fp.seek(offset, whence)
1468 1468 else:
1469 1469 raise NotImplementedError(_('File pointer is not seekable'))
1470 1470
1471 1471 def _tellfp(self):
1472 1472 """return the file offset, or None if file is not seekable
1473 1473
1474 1474 This method is meant for internal usage by the bundle2 protocol only.
1475 1475 They directly manipulate the low level stream including bundle2 level
1476 1476 instruction.
1477 1477
1478 1478 Do not use it to implement higher-level logic or methods."""
1479 1479 if self._seekable:
1480 1480 try:
1481 1481 return self._fp.tell()
1482 1482 except IOError as e:
1483 1483 if e.errno == errno.ESPIPE:
1484 1484 self._seekable = False
1485 1485 else:
1486 1486 raise
1487 1487 return None
1488 1488
1489 1489 # These are only the static capabilities.
1490 1490 # Check the 'getrepocaps' function for the rest.
1491 1491 capabilities = {'HG20': (),
1492 1492 'bookmarks': (),
1493 1493 'error': ('abort', 'unsupportedcontent', 'pushraced',
1494 1494 'pushkey'),
1495 1495 'listkeys': (),
1496 1496 'pushkey': (),
1497 1497 'digests': tuple(sorted(util.DIGESTS.keys())),
1498 1498 'remote-changegroup': ('http', 'https'),
1499 1499 'hgtagsfnodes': (),
1500 1500 'rev-branch-cache': (),
1501 1501 'phases': ('heads',),
1502 1502 'stream': ('v2',),
1503 1503 }
1504 1504
1505 1505 def getrepocaps(repo, allowpushback=False, role=None):
1506 1506 """return the bundle2 capabilities for a given repo
1507 1507
1508 1508 Exists to allow extensions (like evolution) to mutate the capabilities.
1509 1509
1510 1510 The returned value is used for servers advertising their capabilities as
1511 1511 well as clients advertising their capabilities to servers as part of
1512 1512 bundle2 requests. The ``role`` argument specifies which is which.
1513 1513 """
1514 1514 if role not in ('client', 'server'):
1515 1515 raise error.ProgrammingError('role argument must be client or server')
1516 1516
1517 1517 caps = capabilities.copy()
1518 1518 caps['changegroup'] = tuple(sorted(
1519 1519 changegroup.supportedincomingversions(repo)))
1520 1520 if obsolete.isenabled(repo, obsolete.exchangeopt):
1521 1521 supportedformat = tuple('V%i' % v for v in obsolete.formats)
1522 1522 caps['obsmarkers'] = supportedformat
1523 1523 if allowpushback:
1524 1524 caps['pushback'] = ()
1525 1525 cpmode = repo.ui.config('server', 'concurrent-push-mode')
1526 1526 if cpmode == 'check-related':
1527 1527 caps['checkheads'] = ('related',)
1528 1528 if 'phases' in repo.ui.configlist('devel', 'legacy.exchange'):
1529 1529 caps.pop('phases')
1530 1530
1531 1531 # Don't advertise stream clone support in server mode if not configured.
1532 1532 if role == 'server':
1533 1533 streamsupported = repo.ui.configbool('server', 'uncompressed',
1534 1534 untrusted=True)
1535 1535 featuresupported = repo.ui.configbool('server', 'bundle2.stream')
1536 1536
1537 1537 if not streamsupported or not featuresupported:
1538 1538 caps.pop('stream')
1539 1539 # Else always advertise support on client, because payload support
1540 1540 # should always be advertised.
1541 1541
1542 1542 return caps
1543 1543
1544 1544 def bundle2caps(remote):
1545 1545 """return the bundle capabilities of a peer as dict"""
1546 1546 raw = remote.capable('bundle2')
1547 1547 if not raw and raw != '':
1548 1548 return {}
1549 1549 capsblob = urlreq.unquote(remote.capable('bundle2'))
1550 1550 return decodecaps(capsblob)
1551 1551
1552 1552 def obsmarkersversion(caps):
1553 1553 """extract the list of supported obsmarkers versions from a bundle2caps dict
1554 1554 """
1555 1555 obscaps = caps.get('obsmarkers', ())
1556 1556 return [int(c[1:]) for c in obscaps if c.startswith('V')]
1557 1557
1558 1558 def writenewbundle(ui, repo, source, filename, bundletype, outgoing, opts,
1559 1559 vfs=None, compression=None, compopts=None):
1560 1560 if bundletype.startswith('HG10'):
1561 1561 cg = changegroup.makechangegroup(repo, outgoing, '01', source)
1562 1562 return writebundle(ui, cg, filename, bundletype, vfs=vfs,
1563 1563 compression=compression, compopts=compopts)
1564 1564 elif not bundletype.startswith('HG20'):
1565 1565 raise error.ProgrammingError('unknown bundle type: %s' % bundletype)
1566 1566
1567 1567 caps = {}
1568 1568 if 'obsolescence' in opts:
1569 1569 caps['obsmarkers'] = ('V1',)
1570 1570 bundle = bundle20(ui, caps)
1571 1571 bundle.setcompression(compression, compopts)
1572 1572 _addpartsfromopts(ui, repo, bundle, source, outgoing, opts)
1573 1573 chunkiter = bundle.getchunks()
1574 1574
1575 1575 return changegroup.writechunks(ui, chunkiter, filename, vfs=vfs)
1576 1576
1577 1577 def _addpartsfromopts(ui, repo, bundler, source, outgoing, opts):
1578 1578 # We should eventually reconcile this logic with the one behind
1579 1579 # 'exchange.getbundle2partsgenerator'.
1580 1580 #
1581 1581 # The type of input from 'getbundle' and 'writenewbundle' are a bit
1582 1582 # different right now. So we keep them separated for now for the sake of
1583 1583 # simplicity.
1584 1584
1585 1585 # we might not always want a changegroup in such bundle, for example in
1586 1586 # stream bundles
1587 1587 if opts.get('changegroup', True):
1588 1588 cgversion = opts.get('cg.version')
1589 1589 if cgversion is None:
1590 1590 cgversion = changegroup.safeversion(repo)
1591 1591 cg = changegroup.makechangegroup(repo, outgoing, cgversion, source)
1592 1592 part = bundler.newpart('changegroup', data=cg.getchunks())
1593 1593 part.addparam('version', cg.version)
1594 1594 if 'clcount' in cg.extras:
1595 1595 part.addparam('nbchanges', '%d' % cg.extras['clcount'],
1596 1596 mandatory=False)
1597 1597 if opts.get('phases') and repo.revs('%ln and secret()',
1598 1598 outgoing.missingheads):
1599 1599 part.addparam('targetphase', '%d' % phases.secret, mandatory=False)
1600 1600
1601 1601 if opts.get('streamv2', False):
1602 1602 addpartbundlestream2(bundler, repo, stream=True)
1603 1603
1604 1604 if opts.get('tagsfnodescache', True):
1605 1605 addparttagsfnodescache(repo, bundler, outgoing)
1606 1606
1607 1607 if opts.get('revbranchcache', True):
1608 1608 addpartrevbranchcache(repo, bundler, outgoing)
1609 1609
1610 1610 if opts.get('obsolescence', False):
1611 1611 obsmarkers = repo.obsstore.relevantmarkers(outgoing.missing)
1612 1612 buildobsmarkerspart(bundler, obsmarkers)
1613 1613
1614 1614 if opts.get('phases', False):
1615 1615 headsbyphase = phases.subsetphaseheads(repo, outgoing.missing)
1616 1616 phasedata = phases.binaryencode(headsbyphase)
1617 1617 bundler.newpart('phase-heads', data=phasedata)
1618 1618
1619 1619 def addparttagsfnodescache(repo, bundler, outgoing):
1620 1620 # we include the tags fnode cache for the bundle changeset
1621 1621 # (as an optional parts)
1622 1622 cache = tags.hgtagsfnodescache(repo.unfiltered())
1623 1623 chunks = []
1624 1624
1625 1625 # .hgtags fnodes are only relevant for head changesets. While we could
1626 1626 # transfer values for all known nodes, there will likely be little to
1627 1627 # no benefit.
1628 1628 #
1629 1629 # We don't bother using a generator to produce output data because
1630 1630 # a) we only have 40 bytes per head and even esoteric numbers of heads
1631 1631 # consume little memory (1M heads is 40MB) b) we don't want to send the
1632 1632 # part if we don't have entries and knowing if we have entries requires
1633 1633 # cache lookups.
1634 1634 for node in outgoing.missingheads:
1635 1635 # Don't compute missing, as this may slow down serving.
1636 1636 fnode = cache.getfnode(node, computemissing=False)
1637 1637 if fnode is not None:
1638 1638 chunks.extend([node, fnode])
1639 1639
1640 1640 if chunks:
1641 1641 bundler.newpart('hgtagsfnodes', data=''.join(chunks))
1642 1642
1643 1643 def addpartrevbranchcache(repo, bundler, outgoing):
1644 1644 # we include the rev branch cache for the bundle changeset
1645 1645 # (as an optional parts)
1646 1646 cache = repo.revbranchcache()
1647 1647 cl = repo.unfiltered().changelog
1648 1648 branchesdata = collections.defaultdict(lambda: (set(), set()))
1649 1649 for node in outgoing.missing:
1650 1650 branch, close = cache.branchinfo(cl.rev(node))
1651 1651 branchesdata[branch][close].add(node)
1652 1652
1653 1653 def generate():
1654 1654 for branch, (nodes, closed) in sorted(branchesdata.items()):
1655 1655 utf8branch = encoding.fromlocal(branch)
1656 1656 yield rbcstruct.pack(len(utf8branch), len(nodes), len(closed))
1657 1657 yield utf8branch
1658 1658 for n in sorted(nodes):
1659 1659 yield n
1660 1660 for n in sorted(closed):
1661 1661 yield n
1662 1662
1663 1663 bundler.newpart('cache:rev-branch-cache', data=generate(),
1664 1664 mandatory=False)
1665 1665
1666 1666 def _formatrequirementsspec(requirements):
1667 1667 return urlreq.quote(','.join(sorted(requirements)))
1668 1668
1669 1669 def _formatrequirementsparams(requirements):
1670 1670 requirements = _formatrequirementsspec(requirements)
1671 1671 params = "%s%s" % (urlreq.quote("requirements="), requirements)
1672 1672 return params
1673 1673
1674 1674 def addpartbundlestream2(bundler, repo, **kwargs):
1675 1675 if not kwargs.get(r'stream', False):
1676 1676 return
1677 1677
1678 1678 if not streamclone.allowservergeneration(repo):
1679 1679 raise error.Abort(_('stream data requested but server does not allow '
1680 1680 'this feature'),
1681 1681 hint=_('well-behaved clients should not be '
1682 1682 'requesting stream data from servers not '
1683 1683 'advertising it; the client may be buggy'))
1684 1684
1685 1685 # Stream clones don't compress well. And compression undermines a
1686 1686 # goal of stream clones, which is to be fast. Communicate the desire
1687 1687 # to avoid compression to consumers of the bundle.
1688 1688 bundler.prefercompressed = False
1689 1689
1690 1690 filecount, bytecount, it = streamclone.generatev2(repo)
1691 1691 requirements = _formatrequirementsspec(repo.requirements)
1692 1692 part = bundler.newpart('stream2', data=it)
1693 1693 part.addparam('bytecount', '%d' % bytecount, mandatory=True)
1694 1694 part.addparam('filecount', '%d' % filecount, mandatory=True)
1695 1695 part.addparam('requirements', requirements, mandatory=True)
1696 1696
1697 1697 def buildobsmarkerspart(bundler, markers):
1698 1698 """add an obsmarker part to the bundler with <markers>
1699 1699
1700 1700 No part is created if markers is empty.
1701 1701 Raises ValueError if the bundler doesn't support any known obsmarker format.
1702 1702 """
1703 1703 if not markers:
1704 1704 return None
1705 1705
1706 1706 remoteversions = obsmarkersversion(bundler.capabilities)
1707 1707 version = obsolete.commonversion(remoteversions)
1708 1708 if version is None:
1709 1709 raise ValueError('bundler does not support common obsmarker format')
1710 1710 stream = obsolete.encodemarkers(markers, True, version=version)
1711 1711 return bundler.newpart('obsmarkers', data=stream)
1712 1712
1713 1713 def writebundle(ui, cg, filename, bundletype, vfs=None, compression=None,
1714 1714 compopts=None):
1715 1715 """Write a bundle file and return its filename.
1716 1716
1717 1717 Existing files will not be overwritten.
1718 1718 If no filename is specified, a temporary file is created.
1719 1719 bz2 compression can be turned off.
1720 1720 The bundle file will be deleted in case of errors.
1721 1721 """
1722 1722
1723 1723 if bundletype == "HG20":
1724 1724 bundle = bundle20(ui)
1725 1725 bundle.setcompression(compression, compopts)
1726 1726 part = bundle.newpart('changegroup', data=cg.getchunks())
1727 1727 part.addparam('version', cg.version)
1728 1728 if 'clcount' in cg.extras:
1729 1729 part.addparam('nbchanges', '%d' % cg.extras['clcount'],
1730 1730 mandatory=False)
1731 1731 chunkiter = bundle.getchunks()
1732 1732 else:
1733 1733 # compression argument is only for the bundle2 case
1734 1734 assert compression is None
1735 1735 if cg.version != '01':
1736 1736 raise error.Abort(_('old bundle types only supports v1 '
1737 1737 'changegroups'))
1738 1738 header, comp = bundletypes[bundletype]
1739 1739 if comp not in util.compengines.supportedbundletypes:
1740 1740 raise error.Abort(_('unknown stream compression type: %s')
1741 1741 % comp)
1742 1742 compengine = util.compengines.forbundletype(comp)
1743 1743 def chunkiter():
1744 1744 yield header
1745 1745 for chunk in compengine.compressstream(cg.getchunks(), compopts):
1746 1746 yield chunk
1747 1747 chunkiter = chunkiter()
1748 1748
1749 1749 # parse the changegroup data, otherwise we will block
1750 1750 # in case of sshrepo because we don't know the end of the stream
1751 1751 return changegroup.writechunks(ui, chunkiter, filename, vfs=vfs)
1752 1752
1753 1753 def combinechangegroupresults(op):
1754 1754 """logic to combine 0 or more addchangegroup results into one"""
1755 1755 results = [r.get('return', 0)
1756 1756 for r in op.records['changegroup']]
1757 1757 changedheads = 0
1758 1758 result = 1
1759 1759 for ret in results:
1760 1760 # If any changegroup result is 0, return 0
1761 1761 if ret == 0:
1762 1762 result = 0
1763 1763 break
1764 1764 if ret < -1:
1765 1765 changedheads += ret + 1
1766 1766 elif ret > 1:
1767 1767 changedheads += ret - 1
1768 1768 if changedheads > 0:
1769 1769 result = 1 + changedheads
1770 1770 elif changedheads < 0:
1771 1771 result = -1 + changedheads
1772 1772 return result
1773 1773
1774 1774 @parthandler('changegroup', ('version', 'nbchanges', 'treemanifest',
1775 1775 'targetphase'))
1776 1776 def handlechangegroup(op, inpart):
1777 1777 """apply a changegroup part on the repo
1778 1778
1779 1779 This is a very early implementation that will massive rework before being
1780 1780 inflicted to any end-user.
1781 1781 """
1782 1782 from . import localrepo
1783 1783
1784 1784 tr = op.gettransaction()
1785 1785 unpackerversion = inpart.params.get('version', '01')
1786 1786 # We should raise an appropriate exception here
1787 1787 cg = changegroup.getunbundler(unpackerversion, inpart, None)
1788 1788 # the source and url passed here are overwritten by the one contained in
1789 1789 # the transaction.hookargs argument. So 'bundle2' is a placeholder
1790 1790 nbchangesets = None
1791 1791 if 'nbchanges' in inpart.params:
1792 1792 nbchangesets = int(inpart.params.get('nbchanges'))
1793 1793 if ('treemanifest' in inpart.params and
1794 1794 'treemanifest' not in op.repo.requirements):
1795 1795 if len(op.repo.changelog) != 0:
1796 1796 raise error.Abort(_(
1797 1797 "bundle contains tree manifests, but local repo is "
1798 1798 "non-empty and does not use tree manifests"))
1799 1799 op.repo.requirements.add('treemanifest')
1800 1800 op.repo.svfs.options = localrepo.resolvestorevfsoptions(
1801 1801 op.repo.ui, op.repo.requirements, op.repo.features)
1802 1802 op.repo._writerequirements()
1803 1803 extrakwargs = {}
1804 1804 targetphase = inpart.params.get('targetphase')
1805 1805 if targetphase is not None:
1806 1806 extrakwargs[r'targetphase'] = int(targetphase)
1807 1807 ret = _processchangegroup(op, cg, tr, 'bundle2', 'bundle2',
1808 1808 expectedtotal=nbchangesets, **extrakwargs)
1809 1809 if op.reply is not None:
1810 1810 # This is definitely not the final form of this
1811 1811 # return. But one need to start somewhere.
1812 1812 part = op.reply.newpart('reply:changegroup', mandatory=False)
1813 1813 part.addparam(
1814 1814 'in-reply-to', pycompat.bytestr(inpart.id), mandatory=False)
1815 1815 part.addparam('return', '%i' % ret, mandatory=False)
1816 1816 assert not inpart.read()
1817 1817
1818 1818 _remotechangegroupparams = tuple(['url', 'size', 'digests'] +
1819 1819 ['digest:%s' % k for k in util.DIGESTS.keys()])
1820 1820 @parthandler('remote-changegroup', _remotechangegroupparams)
1821 1821 def handleremotechangegroup(op, inpart):
1822 1822 """apply a bundle10 on the repo, given an url and validation information
1823 1823
1824 1824 All the information about the remote bundle to import are given as
1825 1825 parameters. The parameters include:
1826 1826 - url: the url to the bundle10.
1827 1827 - size: the bundle10 file size. It is used to validate what was
1828 1828 retrieved by the client matches the server knowledge about the bundle.
1829 1829 - digests: a space separated list of the digest types provided as
1830 1830 parameters.
1831 1831 - digest:<digest-type>: the hexadecimal representation of the digest with
1832 1832 that name. Like the size, it is used to validate what was retrieved by
1833 1833 the client matches what the server knows about the bundle.
1834 1834
1835 1835 When multiple digest types are given, all of them are checked.
1836 1836 """
1837 1837 try:
1838 1838 raw_url = inpart.params['url']
1839 1839 except KeyError:
1840 1840 raise error.Abort(_('remote-changegroup: missing "%s" param') % 'url')
1841 1841 parsed_url = util.url(raw_url)
1842 1842 if parsed_url.scheme not in capabilities['remote-changegroup']:
1843 1843 raise error.Abort(_('remote-changegroup does not support %s urls') %
1844 1844 parsed_url.scheme)
1845 1845
1846 1846 try:
1847 1847 size = int(inpart.params['size'])
1848 1848 except ValueError:
1849 1849 raise error.Abort(_('remote-changegroup: invalid value for param "%s"')
1850 1850 % 'size')
1851 1851 except KeyError:
1852 1852 raise error.Abort(_('remote-changegroup: missing "%s" param') % 'size')
1853 1853
1854 1854 digests = {}
1855 1855 for typ in inpart.params.get('digests', '').split():
1856 1856 param = 'digest:%s' % typ
1857 1857 try:
1858 1858 value = inpart.params[param]
1859 1859 except KeyError:
1860 1860 raise error.Abort(_('remote-changegroup: missing "%s" param') %
1861 1861 param)
1862 1862 digests[typ] = value
1863 1863
1864 1864 real_part = util.digestchecker(url.open(op.ui, raw_url), size, digests)
1865 1865
1866 1866 tr = op.gettransaction()
1867 1867 from . import exchange
1868 1868 cg = exchange.readbundle(op.repo.ui, real_part, raw_url)
1869 1869 if not isinstance(cg, changegroup.cg1unpacker):
1870 1870 raise error.Abort(_('%s: not a bundle version 1.0') %
1871 1871 util.hidepassword(raw_url))
1872 1872 ret = _processchangegroup(op, cg, tr, 'bundle2', 'bundle2')
1873 1873 if op.reply is not None:
1874 1874 # This is definitely not the final form of this
1875 1875 # return. But one need to start somewhere.
1876 1876 part = op.reply.newpart('reply:changegroup')
1877 1877 part.addparam(
1878 1878 'in-reply-to', pycompat.bytestr(inpart.id), mandatory=False)
1879 1879 part.addparam('return', '%i' % ret, mandatory=False)
1880 1880 try:
1881 1881 real_part.validate()
1882 1882 except error.Abort as e:
1883 1883 raise error.Abort(_('bundle at %s is corrupted:\n%s') %
1884 1884 (util.hidepassword(raw_url), bytes(e)))
1885 1885 assert not inpart.read()
1886 1886
1887 1887 @parthandler('reply:changegroup', ('return', 'in-reply-to'))
1888 1888 def handlereplychangegroup(op, inpart):
1889 1889 ret = int(inpart.params['return'])
1890 1890 replyto = int(inpart.params['in-reply-to'])
1891 1891 op.records.add('changegroup', {'return': ret}, replyto)
1892 1892
1893 1893 @parthandler('check:bookmarks')
1894 1894 def handlecheckbookmarks(op, inpart):
1895 1895 """check location of bookmarks
1896 1896
1897 1897 This part is to be used to detect push race regarding bookmark, it
1898 1898 contains binary encoded (bookmark, node) tuple. If the local state does
1899 1899 not marks the one in the part, a PushRaced exception is raised
1900 1900 """
1901 1901 bookdata = bookmarks.binarydecode(inpart)
1902 1902
1903 msgstandard = ('repository changed while pushing - please try again '
1903 msgstandard = ('remote repository changed while pushing - please try again '
1904 1904 '(bookmark "%s" move from %s to %s)')
1905 msgmissing = ('repository changed while pushing - please try again '
1905 msgmissing = ('remote repository changed while pushing - please try again '
1906 1906 '(bookmark "%s" is missing, expected %s)')
1907 msgexist = ('repository changed while pushing - please try again '
1907 msgexist = ('remote repository changed while pushing - please try again '
1908 1908 '(bookmark "%s" set on %s, expected missing)')
1909 1909 for book, node in bookdata:
1910 1910 currentnode = op.repo._bookmarks.get(book)
1911 1911 if currentnode != node:
1912 1912 if node is None:
1913 1913 finalmsg = msgexist % (book, nodemod.short(currentnode))
1914 1914 elif currentnode is None:
1915 1915 finalmsg = msgmissing % (book, nodemod.short(node))
1916 1916 else:
1917 1917 finalmsg = msgstandard % (book, nodemod.short(node),
1918 1918 nodemod.short(currentnode))
1919 1919 raise error.PushRaced(finalmsg)
1920 1920
1921 1921 @parthandler('check:heads')
1922 1922 def handlecheckheads(op, inpart):
1923 1923 """check that head of the repo did not change
1924 1924
1925 1925 This is used to detect a push race when using unbundle.
1926 1926 This replaces the "heads" argument of unbundle."""
1927 1927 h = inpart.read(20)
1928 1928 heads = []
1929 1929 while len(h) == 20:
1930 1930 heads.append(h)
1931 1931 h = inpart.read(20)
1932 1932 assert not h
1933 1933 # Trigger a transaction so that we are guaranteed to have the lock now.
1934 1934 if op.ui.configbool('experimental', 'bundle2lazylocking'):
1935 1935 op.gettransaction()
1936 1936 if sorted(heads) != sorted(op.repo.heads()):
1937 raise error.PushRaced('repository changed while pushing - '
1937 raise error.PushRaced('remote repository changed while pushing - '
1938 1938 'please try again')
1939 1939
1940 1940 @parthandler('check:updated-heads')
1941 1941 def handlecheckupdatedheads(op, inpart):
1942 1942 """check for race on the heads touched by a push
1943 1943
1944 1944 This is similar to 'check:heads' but focus on the heads actually updated
1945 1945 during the push. If other activities happen on unrelated heads, it is
1946 1946 ignored.
1947 1947
1948 1948 This allow server with high traffic to avoid push contention as long as
1949 1949 unrelated parts of the graph are involved."""
1950 1950 h = inpart.read(20)
1951 1951 heads = []
1952 1952 while len(h) == 20:
1953 1953 heads.append(h)
1954 1954 h = inpart.read(20)
1955 1955 assert not h
1956 1956 # trigger a transaction so that we are guaranteed to have the lock now.
1957 1957 if op.ui.configbool('experimental', 'bundle2lazylocking'):
1958 1958 op.gettransaction()
1959 1959
1960 1960 currentheads = set()
1961 1961 for ls in op.repo.branchmap().itervalues():
1962 1962 currentheads.update(ls)
1963 1963
1964 1964 for h in heads:
1965 1965 if h not in currentheads:
1966 raise error.PushRaced('repository changed while pushing - '
1966 raise error.PushRaced('remote repository changed while pushing - '
1967 1967 'please try again')
1968 1968
1969 1969 @parthandler('check:phases')
1970 1970 def handlecheckphases(op, inpart):
1971 1971 """check that phase boundaries of the repository did not change
1972 1972
1973 1973 This is used to detect a push race.
1974 1974 """
1975 1975 phasetonodes = phases.binarydecode(inpart)
1976 1976 unfi = op.repo.unfiltered()
1977 1977 cl = unfi.changelog
1978 1978 phasecache = unfi._phasecache
1979 msg = ('repository changed while pushing - please try again '
1979 msg = ('remote repository changed while pushing - please try again '
1980 1980 '(%s is %s expected %s)')
1981 1981 for expectedphase, nodes in enumerate(phasetonodes):
1982 1982 for n in nodes:
1983 1983 actualphase = phasecache.phase(unfi, cl.rev(n))
1984 1984 if actualphase != expectedphase:
1985 1985 finalmsg = msg % (nodemod.short(n),
1986 1986 phases.phasenames[actualphase],
1987 1987 phases.phasenames[expectedphase])
1988 1988 raise error.PushRaced(finalmsg)
1989 1989
1990 1990 @parthandler('output')
1991 1991 def handleoutput(op, inpart):
1992 1992 """forward output captured on the server to the client"""
1993 1993 for line in inpart.read().splitlines():
1994 1994 op.ui.status(_('remote: %s\n') % line)
1995 1995
1996 1996 @parthandler('replycaps')
1997 1997 def handlereplycaps(op, inpart):
1998 1998 """Notify that a reply bundle should be created
1999 1999
2000 2000 The payload contains the capabilities information for the reply"""
2001 2001 caps = decodecaps(inpart.read())
2002 2002 if op.reply is None:
2003 2003 op.reply = bundle20(op.ui, caps)
2004 2004
2005 2005 class AbortFromPart(error.Abort):
2006 2006 """Sub-class of Abort that denotes an error from a bundle2 part."""
2007 2007
2008 2008 @parthandler('error:abort', ('message', 'hint'))
2009 2009 def handleerrorabort(op, inpart):
2010 2010 """Used to transmit abort error over the wire"""
2011 2011 raise AbortFromPart(inpart.params['message'],
2012 2012 hint=inpart.params.get('hint'))
2013 2013
2014 2014 @parthandler('error:pushkey', ('namespace', 'key', 'new', 'old', 'ret',
2015 2015 'in-reply-to'))
2016 2016 def handleerrorpushkey(op, inpart):
2017 2017 """Used to transmit failure of a mandatory pushkey over the wire"""
2018 2018 kwargs = {}
2019 2019 for name in ('namespace', 'key', 'new', 'old', 'ret'):
2020 2020 value = inpart.params.get(name)
2021 2021 if value is not None:
2022 2022 kwargs[name] = value
2023 2023 raise error.PushkeyFailed(inpart.params['in-reply-to'],
2024 2024 **pycompat.strkwargs(kwargs))
2025 2025
2026 2026 @parthandler('error:unsupportedcontent', ('parttype', 'params'))
2027 2027 def handleerrorunsupportedcontent(op, inpart):
2028 2028 """Used to transmit unknown content error over the wire"""
2029 2029 kwargs = {}
2030 2030 parttype = inpart.params.get('parttype')
2031 2031 if parttype is not None:
2032 2032 kwargs['parttype'] = parttype
2033 2033 params = inpart.params.get('params')
2034 2034 if params is not None:
2035 2035 kwargs['params'] = params.split('\0')
2036 2036
2037 2037 raise error.BundleUnknownFeatureError(**pycompat.strkwargs(kwargs))
2038 2038
2039 2039 @parthandler('error:pushraced', ('message',))
2040 2040 def handleerrorpushraced(op, inpart):
2041 2041 """Used to transmit push race error over the wire"""
2042 2042 raise error.ResponseError(_('push failed:'), inpart.params['message'])
2043 2043
2044 2044 @parthandler('listkeys', ('namespace',))
2045 2045 def handlelistkeys(op, inpart):
2046 2046 """retrieve pushkey namespace content stored in a bundle2"""
2047 2047 namespace = inpart.params['namespace']
2048 2048 r = pushkey.decodekeys(inpart.read())
2049 2049 op.records.add('listkeys', (namespace, r))
2050 2050
2051 2051 @parthandler('pushkey', ('namespace', 'key', 'old', 'new'))
2052 2052 def handlepushkey(op, inpart):
2053 2053 """process a pushkey request"""
2054 2054 dec = pushkey.decode
2055 2055 namespace = dec(inpart.params['namespace'])
2056 2056 key = dec(inpart.params['key'])
2057 2057 old = dec(inpart.params['old'])
2058 2058 new = dec(inpart.params['new'])
2059 2059 # Grab the transaction to ensure that we have the lock before performing the
2060 2060 # pushkey.
2061 2061 if op.ui.configbool('experimental', 'bundle2lazylocking'):
2062 2062 op.gettransaction()
2063 2063 ret = op.repo.pushkey(namespace, key, old, new)
2064 2064 record = {'namespace': namespace,
2065 2065 'key': key,
2066 2066 'old': old,
2067 2067 'new': new}
2068 2068 op.records.add('pushkey', record)
2069 2069 if op.reply is not None:
2070 2070 rpart = op.reply.newpart('reply:pushkey')
2071 2071 rpart.addparam(
2072 2072 'in-reply-to', pycompat.bytestr(inpart.id), mandatory=False)
2073 2073 rpart.addparam('return', '%i' % ret, mandatory=False)
2074 2074 if inpart.mandatory and not ret:
2075 2075 kwargs = {}
2076 2076 for key in ('namespace', 'key', 'new', 'old', 'ret'):
2077 2077 if key in inpart.params:
2078 2078 kwargs[key] = inpart.params[key]
2079 2079 raise error.PushkeyFailed(partid='%d' % inpart.id,
2080 2080 **pycompat.strkwargs(kwargs))
2081 2081
2082 2082 @parthandler('bookmarks')
2083 2083 def handlebookmark(op, inpart):
2084 2084 """transmit bookmark information
2085 2085
2086 2086 The part contains binary encoded bookmark information.
2087 2087
2088 2088 The exact behavior of this part can be controlled by the 'bookmarks' mode
2089 2089 on the bundle operation.
2090 2090
2091 2091 When mode is 'apply' (the default) the bookmark information is applied as
2092 2092 is to the unbundling repository. Make sure a 'check:bookmarks' part is
2093 2093 issued earlier to check for push races in such update. This behavior is
2094 2094 suitable for pushing.
2095 2095
2096 2096 When mode is 'records', the information is recorded into the 'bookmarks'
2097 2097 records of the bundle operation. This behavior is suitable for pulling.
2098 2098 """
2099 2099 changes = bookmarks.binarydecode(inpart)
2100 2100
2101 2101 pushkeycompat = op.repo.ui.configbool('server', 'bookmarks-pushkey-compat')
2102 2102 bookmarksmode = op.modes.get('bookmarks', 'apply')
2103 2103
2104 2104 if bookmarksmode == 'apply':
2105 2105 tr = op.gettransaction()
2106 2106 bookstore = op.repo._bookmarks
2107 2107 if pushkeycompat:
2108 2108 allhooks = []
2109 2109 for book, node in changes:
2110 2110 hookargs = tr.hookargs.copy()
2111 2111 hookargs['pushkeycompat'] = '1'
2112 2112 hookargs['namespace'] = 'bookmarks'
2113 2113 hookargs['key'] = book
2114 2114 hookargs['old'] = nodemod.hex(bookstore.get(book, ''))
2115 2115 hookargs['new'] = nodemod.hex(node if node is not None else '')
2116 2116 allhooks.append(hookargs)
2117 2117
2118 2118 for hookargs in allhooks:
2119 2119 op.repo.hook('prepushkey', throw=True,
2120 2120 **pycompat.strkwargs(hookargs))
2121 2121
2122 2122 bookstore.applychanges(op.repo, op.gettransaction(), changes)
2123 2123
2124 2124 if pushkeycompat:
2125 2125 def runhook():
2126 2126 for hookargs in allhooks:
2127 2127 op.repo.hook('pushkey', **pycompat.strkwargs(hookargs))
2128 2128 op.repo._afterlock(runhook)
2129 2129
2130 2130 elif bookmarksmode == 'records':
2131 2131 for book, node in changes:
2132 2132 record = {'bookmark': book, 'node': node}
2133 2133 op.records.add('bookmarks', record)
2134 2134 else:
2135 2135 raise error.ProgrammingError('unkown bookmark mode: %s' % bookmarksmode)
2136 2136
2137 2137 @parthandler('phase-heads')
2138 2138 def handlephases(op, inpart):
2139 2139 """apply phases from bundle part to repo"""
2140 2140 headsbyphase = phases.binarydecode(inpart)
2141 2141 phases.updatephases(op.repo.unfiltered(), op.gettransaction, headsbyphase)
2142 2142
2143 2143 @parthandler('reply:pushkey', ('return', 'in-reply-to'))
2144 2144 def handlepushkeyreply(op, inpart):
2145 2145 """retrieve the result of a pushkey request"""
2146 2146 ret = int(inpart.params['return'])
2147 2147 partid = int(inpart.params['in-reply-to'])
2148 2148 op.records.add('pushkey', {'return': ret}, partid)
2149 2149
2150 2150 @parthandler('obsmarkers')
2151 2151 def handleobsmarker(op, inpart):
2152 2152 """add a stream of obsmarkers to the repo"""
2153 2153 tr = op.gettransaction()
2154 2154 markerdata = inpart.read()
2155 2155 if op.ui.config('experimental', 'obsmarkers-exchange-debug'):
2156 2156 op.ui.write(('obsmarker-exchange: %i bytes received\n')
2157 2157 % len(markerdata))
2158 2158 # The mergemarkers call will crash if marker creation is not enabled.
2159 2159 # we want to avoid this if the part is advisory.
2160 2160 if not inpart.mandatory and op.repo.obsstore.readonly:
2161 2161 op.repo.ui.debug('ignoring obsolescence markers, feature not enabled\n')
2162 2162 return
2163 2163 new = op.repo.obsstore.mergemarkers(tr, markerdata)
2164 2164 op.repo.invalidatevolatilesets()
2165 2165 if new:
2166 2166 op.repo.ui.status(_('%i new obsolescence markers\n') % new)
2167 2167 op.records.add('obsmarkers', {'new': new})
2168 2168 if op.reply is not None:
2169 2169 rpart = op.reply.newpart('reply:obsmarkers')
2170 2170 rpart.addparam(
2171 2171 'in-reply-to', pycompat.bytestr(inpart.id), mandatory=False)
2172 2172 rpart.addparam('new', '%i' % new, mandatory=False)
2173 2173
2174 2174
2175 2175 @parthandler('reply:obsmarkers', ('new', 'in-reply-to'))
2176 2176 def handleobsmarkerreply(op, inpart):
2177 2177 """retrieve the result of a pushkey request"""
2178 2178 ret = int(inpart.params['new'])
2179 2179 partid = int(inpart.params['in-reply-to'])
2180 2180 op.records.add('obsmarkers', {'new': ret}, partid)
2181 2181
2182 2182 @parthandler('hgtagsfnodes')
2183 2183 def handlehgtagsfnodes(op, inpart):
2184 2184 """Applies .hgtags fnodes cache entries to the local repo.
2185 2185
2186 2186 Payload is pairs of 20 byte changeset nodes and filenodes.
2187 2187 """
2188 2188 # Grab the transaction so we ensure that we have the lock at this point.
2189 2189 if op.ui.configbool('experimental', 'bundle2lazylocking'):
2190 2190 op.gettransaction()
2191 2191 cache = tags.hgtagsfnodescache(op.repo.unfiltered())
2192 2192
2193 2193 count = 0
2194 2194 while True:
2195 2195 node = inpart.read(20)
2196 2196 fnode = inpart.read(20)
2197 2197 if len(node) < 20 or len(fnode) < 20:
2198 2198 op.ui.debug('ignoring incomplete received .hgtags fnodes data\n')
2199 2199 break
2200 2200 cache.setfnode(node, fnode)
2201 2201 count += 1
2202 2202
2203 2203 cache.write()
2204 2204 op.ui.debug('applied %i hgtags fnodes cache entries\n' % count)
2205 2205
2206 2206 rbcstruct = struct.Struct('>III')
2207 2207
2208 2208 @parthandler('cache:rev-branch-cache')
2209 2209 def handlerbc(op, inpart):
2210 2210 """receive a rev-branch-cache payload and update the local cache
2211 2211
2212 2212 The payload is a series of data related to each branch
2213 2213
2214 2214 1) branch name length
2215 2215 2) number of open heads
2216 2216 3) number of closed heads
2217 2217 4) open heads nodes
2218 2218 5) closed heads nodes
2219 2219 """
2220 2220 total = 0
2221 2221 rawheader = inpart.read(rbcstruct.size)
2222 2222 cache = op.repo.revbranchcache()
2223 2223 cl = op.repo.unfiltered().changelog
2224 2224 while rawheader:
2225 2225 header = rbcstruct.unpack(rawheader)
2226 2226 total += header[1] + header[2]
2227 2227 utf8branch = inpart.read(header[0])
2228 2228 branch = encoding.tolocal(utf8branch)
2229 2229 for x in pycompat.xrange(header[1]):
2230 2230 node = inpart.read(20)
2231 2231 rev = cl.rev(node)
2232 2232 cache.setdata(branch, rev, node, False)
2233 2233 for x in pycompat.xrange(header[2]):
2234 2234 node = inpart.read(20)
2235 2235 rev = cl.rev(node)
2236 2236 cache.setdata(branch, rev, node, True)
2237 2237 rawheader = inpart.read(rbcstruct.size)
2238 2238 cache.write()
2239 2239
2240 2240 @parthandler('pushvars')
2241 2241 def bundle2getvars(op, part):
2242 2242 '''unbundle a bundle2 containing shellvars on the server'''
2243 2243 # An option to disable unbundling on server-side for security reasons
2244 2244 if op.ui.configbool('push', 'pushvars.server'):
2245 2245 hookargs = {}
2246 2246 for key, value in part.advisoryparams:
2247 2247 key = key.upper()
2248 2248 # We want pushed variables to have USERVAR_ prepended so we know
2249 2249 # they came from the --pushvar flag.
2250 2250 key = "USERVAR_" + key
2251 2251 hookargs[key] = value
2252 2252 op.addhookargs(hookargs)
2253 2253
2254 2254 @parthandler('stream2', ('requirements', 'filecount', 'bytecount'))
2255 2255 def handlestreamv2bundle(op, part):
2256 2256
2257 2257 requirements = urlreq.unquote(part.params['requirements']).split(',')
2258 2258 filecount = int(part.params['filecount'])
2259 2259 bytecount = int(part.params['bytecount'])
2260 2260
2261 2261 repo = op.repo
2262 2262 if len(repo):
2263 2263 msg = _('cannot apply stream clone to non empty repository')
2264 2264 raise error.Abort(msg)
2265 2265
2266 2266 repo.ui.debug('applying stream bundle\n')
2267 2267 streamclone.applybundlev2(repo, part, filecount, bytecount,
2268 2268 requirements)
2269 2269
2270 2270 def widen_bundle(repo, diffmatcher, common, known, cgversion, ellipses):
2271 2271 """generates bundle2 for widening a narrow clone
2272 2272
2273 2273 repo is the localrepository instance
2274 2274 diffmatcher is a differencemacther of '(newincludes, newexcludes) -
2275 2275 (oldincludes, oldexcludes)'
2276 2276 common is set of common heads between server and client
2277 2277 known is a set of revs known on the client side (used in ellipses)
2278 2278 cgversion is the changegroup version to send
2279 2279 ellipses is boolean value telling whether to send ellipses data or not
2280 2280
2281 2281 returns bundle2 of the data required for extending
2282 2282 """
2283 2283 bundler = bundle20(repo.ui)
2284 2284 commonnodes = set()
2285 2285 cl = repo.changelog
2286 2286 for r in repo.revs("::%ln", common):
2287 2287 commonnodes.add(cl.node(r))
2288 2288 if commonnodes:
2289 2289 # XXX: we should only send the filelogs (and treemanifest). user
2290 2290 # already has the changelog and manifest
2291 2291 packer = changegroup.getbundler(cgversion, repo,
2292 2292 filematcher=diffmatcher,
2293 2293 fullnodes=commonnodes)
2294 2294 cgdata = packer.generate(set([nodemod.nullid]), list(commonnodes),
2295 2295 False, 'narrow_widen', changelog=False)
2296 2296
2297 2297 part = bundler.newpart('changegroup', data=cgdata)
2298 2298 part.addparam('version', cgversion)
2299 2299 if 'treemanifest' in repo.requirements:
2300 2300 part.addparam('treemanifest', '1')
2301 2301
2302 2302 return bundler
@@ -1,1159 +1,1159
1 1 #testcases sshv1 sshv2
2 2
3 3 #if sshv2
4 4 $ cat >> $HGRCPATH << EOF
5 5 > [experimental]
6 6 > sshpeer.advertise-v2 = true
7 7 > sshserver.support-v2 = true
8 8 > EOF
9 9 #endif
10 10
11 11 Test exchange of common information using bundle2
12 12
13 13
14 14 $ getmainid() {
15 15 > hg -R main log --template '{node}\n' --rev "$1"
16 16 > }
17 17
18 18 enable obsolescence
19 19
20 20 $ cp $HGRCPATH $TESTTMP/hgrc.orig
21 21 $ cat > $TESTTMP/bundle2-pushkey-hook.sh << EOF
22 22 > echo pushkey: lock state after \"\$HG_NAMESPACE\"
23 23 > hg debuglock
24 24 > EOF
25 25
26 26 $ cat >> $HGRCPATH << EOF
27 27 > [experimental]
28 28 > evolution.createmarkers=True
29 29 > evolution.exchange=True
30 30 > bundle2-output-capture=True
31 31 > [ui]
32 32 > ssh="$PYTHON" "$TESTDIR/dummyssh"
33 33 > logtemplate={rev}:{node|short} {phase} {author} {bookmarks} {desc|firstline}
34 34 > [web]
35 35 > push_ssl = false
36 36 > allow_push = *
37 37 > [phases]
38 38 > publish=False
39 39 > [hooks]
40 40 > pretxnclose.tip = hg log -r tip -T "pre-close-tip:{node|short} {phase} {bookmarks}\n"
41 41 > txnclose.tip = hg log -r tip -T "postclose-tip:{node|short} {phase} {bookmarks}\n"
42 42 > txnclose.env = sh -c "HG_LOCAL= printenv.py txnclose"
43 43 > pushkey= sh "$TESTTMP/bundle2-pushkey-hook.sh"
44 44 > EOF
45 45
46 46 The extension requires a repo (currently unused)
47 47
48 48 $ hg init main
49 49 $ cd main
50 50 $ touch a
51 51 $ hg add a
52 52 $ hg commit -m 'a'
53 53 pre-close-tip:3903775176ed draft
54 54 postclose-tip:3903775176ed draft
55 55 txnclose hook: HG_HOOKNAME=txnclose.env HG_HOOKTYPE=txnclose HG_PHASES_MOVED=1 HG_TXNID=TXN:$ID$ HG_TXNNAME=commit
56 56
57 57 $ hg unbundle $TESTDIR/bundles/rebase.hg
58 58 adding changesets
59 59 adding manifests
60 60 adding file changes
61 61 added 8 changesets with 7 changes to 7 files (+3 heads)
62 62 pre-close-tip:02de42196ebe draft
63 63 new changesets cd010b8cd998:02de42196ebe (8 drafts)
64 64 postclose-tip:02de42196ebe draft
65 65 txnclose hook: HG_HOOKNAME=txnclose.env HG_HOOKTYPE=txnclose HG_NODE=cd010b8cd998f3981a5a8115f94f8da4ab506089 HG_NODE_LAST=02de42196ebee42ef284b6780a87cdc96e8eaab6 HG_PHASES_MOVED=1 HG_SOURCE=unbundle HG_TXNID=TXN:$ID$ HG_TXNNAME=unbundle
66 66 bundle:*/tests/bundles/rebase.hg HG_URL=bundle:*/tests/bundles/rebase.hg (glob)
67 67 (run 'hg heads' to see heads, 'hg merge' to merge)
68 68
69 69 $ cd ..
70 70
71 71 Real world exchange
72 72 =====================
73 73
74 74 Add more obsolescence information
75 75
76 76 $ hg -R main debugobsolete -d '0 0' 1111111111111111111111111111111111111111 `getmainid 9520eea781bc`
77 77 pre-close-tip:02de42196ebe draft
78 78 postclose-tip:02de42196ebe draft
79 79 txnclose hook: HG_HOOKNAME=txnclose.env HG_HOOKTYPE=txnclose HG_NEW_OBSMARKERS=1 HG_TXNID=TXN:$ID$ HG_TXNNAME=debugobsolete
80 80 $ hg -R main debugobsolete -d '0 0' 2222222222222222222222222222222222222222 `getmainid 24b6387c8c8c`
81 81 pre-close-tip:02de42196ebe draft
82 82 postclose-tip:02de42196ebe draft
83 83 txnclose hook: HG_HOOKNAME=txnclose.env HG_HOOKTYPE=txnclose HG_NEW_OBSMARKERS=1 HG_TXNID=TXN:$ID$ HG_TXNNAME=debugobsolete
84 84
85 85 clone --pull
86 86
87 87 $ hg -R main phase --public cd010b8cd998
88 88 pre-close-tip:02de42196ebe draft
89 89 postclose-tip:02de42196ebe draft
90 90 txnclose hook: HG_HOOKNAME=txnclose.env HG_HOOKTYPE=txnclose HG_PHASES_MOVED=1 HG_TXNID=TXN:$ID$ HG_TXNNAME=phase
91 91 $ hg clone main other --pull --rev 9520eea781bc
92 92 adding changesets
93 93 adding manifests
94 94 adding file changes
95 95 added 2 changesets with 2 changes to 2 files
96 96 1 new obsolescence markers
97 97 pre-close-tip:9520eea781bc draft
98 98 new changesets cd010b8cd998:9520eea781bc (1 drafts)
99 99 postclose-tip:9520eea781bc draft
100 100 txnclose hook: HG_HOOKNAME=txnclose.env HG_HOOKTYPE=txnclose HG_NEW_OBSMARKERS=1 HG_NODE=cd010b8cd998f3981a5a8115f94f8da4ab506089 HG_NODE_LAST=9520eea781bcca16c1e15acc0ba14335a0e8e5ba HG_PHASES_MOVED=1 HG_SOURCE=pull HG_TXNID=TXN:$ID$ HG_TXNNAME=pull
101 101 file:/*/$TESTTMP/main HG_URL=file:$TESTTMP/main (glob)
102 102 updating to branch default
103 103 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
104 104 $ hg -R other log -G
105 105 @ 1:9520eea781bc draft Nicolas Dumazet <nicdumz.commits@gmail.com> E
106 106 |
107 107 o 0:cd010b8cd998 public Nicolas Dumazet <nicdumz.commits@gmail.com> A
108 108
109 109 $ hg -R other debugobsolete
110 110 1111111111111111111111111111111111111111 9520eea781bcca16c1e15acc0ba14335a0e8e5ba 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
111 111
112 112 pull
113 113
114 114 $ hg -R main phase --public 9520eea781bc
115 115 pre-close-tip:02de42196ebe draft
116 116 postclose-tip:02de42196ebe draft
117 117 txnclose hook: HG_HOOKNAME=txnclose.env HG_HOOKTYPE=txnclose HG_PHASES_MOVED=1 HG_TXNID=TXN:$ID$ HG_TXNNAME=phase
118 118 $ hg -R other pull -r 24b6387c8c8c
119 119 pulling from $TESTTMP/main
120 120 searching for changes
121 121 adding changesets
122 122 adding manifests
123 123 adding file changes
124 124 added 1 changesets with 1 changes to 1 files (+1 heads)
125 125 1 new obsolescence markers
126 126 pre-close-tip:24b6387c8c8c draft
127 127 new changesets 24b6387c8c8c (1 drafts)
128 128 postclose-tip:24b6387c8c8c draft
129 129 txnclose hook: HG_HOOKNAME=txnclose.env HG_HOOKTYPE=txnclose HG_NEW_OBSMARKERS=1 HG_NODE=24b6387c8c8cae37178880f3fa95ded3cb1cf785 HG_NODE_LAST=24b6387c8c8cae37178880f3fa95ded3cb1cf785 HG_PHASES_MOVED=1 HG_SOURCE=pull HG_TXNID=TXN:$ID$ HG_TXNNAME=pull
130 130 file:/*/$TESTTMP/main HG_URL=file:$TESTTMP/main (glob)
131 131 (run 'hg heads' to see heads, 'hg merge' to merge)
132 132 $ hg -R other log -G
133 133 o 2:24b6387c8c8c draft Nicolas Dumazet <nicdumz.commits@gmail.com> F
134 134 |
135 135 | @ 1:9520eea781bc draft Nicolas Dumazet <nicdumz.commits@gmail.com> E
136 136 |/
137 137 o 0:cd010b8cd998 public Nicolas Dumazet <nicdumz.commits@gmail.com> A
138 138
139 139 $ hg -R other debugobsolete
140 140 1111111111111111111111111111111111111111 9520eea781bcca16c1e15acc0ba14335a0e8e5ba 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
141 141 2222222222222222222222222222222222222222 24b6387c8c8cae37178880f3fa95ded3cb1cf785 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
142 142
143 143 pull empty (with phase movement)
144 144
145 145 $ hg -R main phase --public 24b6387c8c8c
146 146 pre-close-tip:02de42196ebe draft
147 147 postclose-tip:02de42196ebe draft
148 148 txnclose hook: HG_HOOKNAME=txnclose.env HG_HOOKTYPE=txnclose HG_PHASES_MOVED=1 HG_TXNID=TXN:$ID$ HG_TXNNAME=phase
149 149 $ hg -R other pull -r 24b6387c8c8c
150 150 pulling from $TESTTMP/main
151 151 no changes found
152 152 pre-close-tip:24b6387c8c8c public
153 153 1 local changesets published
154 154 postclose-tip:24b6387c8c8c public
155 155 txnclose hook: HG_HOOKNAME=txnclose.env HG_HOOKTYPE=txnclose HG_NEW_OBSMARKERS=0 HG_PHASES_MOVED=1 HG_SOURCE=pull HG_TXNID=TXN:$ID$ HG_TXNNAME=pull
156 156 file:/*/$TESTTMP/main HG_URL=file:$TESTTMP/main (glob)
157 157 $ hg -R other log -G
158 158 o 2:24b6387c8c8c public Nicolas Dumazet <nicdumz.commits@gmail.com> F
159 159 |
160 160 | @ 1:9520eea781bc draft Nicolas Dumazet <nicdumz.commits@gmail.com> E
161 161 |/
162 162 o 0:cd010b8cd998 public Nicolas Dumazet <nicdumz.commits@gmail.com> A
163 163
164 164 $ hg -R other debugobsolete
165 165 1111111111111111111111111111111111111111 9520eea781bcca16c1e15acc0ba14335a0e8e5ba 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
166 166 2222222222222222222222222222222222222222 24b6387c8c8cae37178880f3fa95ded3cb1cf785 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
167 167
168 168 pull empty
169 169
170 170 $ hg -R other pull -r 24b6387c8c8c
171 171 pulling from $TESTTMP/main
172 172 no changes found
173 173 pre-close-tip:24b6387c8c8c public
174 174 postclose-tip:24b6387c8c8c public
175 175 txnclose hook: HG_HOOKNAME=txnclose.env HG_HOOKTYPE=txnclose HG_NEW_OBSMARKERS=0 HG_SOURCE=pull HG_TXNID=TXN:$ID$ HG_TXNNAME=pull
176 176 file:/*/$TESTTMP/main HG_URL=file:$TESTTMP/main (glob)
177 177 $ hg -R other log -G
178 178 o 2:24b6387c8c8c public Nicolas Dumazet <nicdumz.commits@gmail.com> F
179 179 |
180 180 | @ 1:9520eea781bc draft Nicolas Dumazet <nicdumz.commits@gmail.com> E
181 181 |/
182 182 o 0:cd010b8cd998 public Nicolas Dumazet <nicdumz.commits@gmail.com> A
183 183
184 184 $ hg -R other debugobsolete
185 185 1111111111111111111111111111111111111111 9520eea781bcca16c1e15acc0ba14335a0e8e5ba 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
186 186 2222222222222222222222222222222222222222 24b6387c8c8cae37178880f3fa95ded3cb1cf785 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
187 187
188 188 add extra data to test their exchange during push
189 189
190 190 $ hg -R main bookmark --rev eea13746799a book_eea1
191 191 pre-close-tip:02de42196ebe draft
192 192 postclose-tip:02de42196ebe draft
193 193 txnclose hook: HG_BOOKMARK_MOVED=1 HG_HOOKNAME=txnclose.env HG_HOOKTYPE=txnclose HG_TXNID=TXN:$ID$ HG_TXNNAME=bookmark
194 194 $ hg -R main debugobsolete -d '0 0' 3333333333333333333333333333333333333333 `getmainid eea13746799a`
195 195 pre-close-tip:02de42196ebe draft
196 196 postclose-tip:02de42196ebe draft
197 197 txnclose hook: HG_HOOKNAME=txnclose.env HG_HOOKTYPE=txnclose HG_NEW_OBSMARKERS=1 HG_TXNID=TXN:$ID$ HG_TXNNAME=debugobsolete
198 198 $ hg -R main bookmark --rev 02de42196ebe book_02de
199 199 pre-close-tip:02de42196ebe draft book_02de
200 200 postclose-tip:02de42196ebe draft book_02de
201 201 txnclose hook: HG_BOOKMARK_MOVED=1 HG_HOOKNAME=txnclose.env HG_HOOKTYPE=txnclose HG_TXNID=TXN:$ID$ HG_TXNNAME=bookmark
202 202 $ hg -R main debugobsolete -d '0 0' 4444444444444444444444444444444444444444 `getmainid 02de42196ebe`
203 203 pre-close-tip:02de42196ebe draft book_02de
204 204 postclose-tip:02de42196ebe draft book_02de
205 205 txnclose hook: HG_HOOKNAME=txnclose.env HG_HOOKTYPE=txnclose HG_NEW_OBSMARKERS=1 HG_TXNID=TXN:$ID$ HG_TXNNAME=debugobsolete
206 206 $ hg -R main bookmark --rev 42ccdea3bb16 book_42cc
207 207 pre-close-tip:02de42196ebe draft book_02de
208 208 postclose-tip:02de42196ebe draft book_02de
209 209 txnclose hook: HG_BOOKMARK_MOVED=1 HG_HOOKNAME=txnclose.env HG_HOOKTYPE=txnclose HG_TXNID=TXN:$ID$ HG_TXNNAME=bookmark
210 210 $ hg -R main debugobsolete -d '0 0' 5555555555555555555555555555555555555555 `getmainid 42ccdea3bb16`
211 211 pre-close-tip:02de42196ebe draft book_02de
212 212 postclose-tip:02de42196ebe draft book_02de
213 213 txnclose hook: HG_HOOKNAME=txnclose.env HG_HOOKTYPE=txnclose HG_NEW_OBSMARKERS=1 HG_TXNID=TXN:$ID$ HG_TXNNAME=debugobsolete
214 214 $ hg -R main bookmark --rev 5fddd98957c8 book_5fdd
215 215 pre-close-tip:02de42196ebe draft book_02de
216 216 postclose-tip:02de42196ebe draft book_02de
217 217 txnclose hook: HG_BOOKMARK_MOVED=1 HG_HOOKNAME=txnclose.env HG_HOOKTYPE=txnclose HG_TXNID=TXN:$ID$ HG_TXNNAME=bookmark
218 218 $ hg -R main debugobsolete -d '0 0' 6666666666666666666666666666666666666666 `getmainid 5fddd98957c8`
219 219 pre-close-tip:02de42196ebe draft book_02de
220 220 postclose-tip:02de42196ebe draft book_02de
221 221 txnclose hook: HG_HOOKNAME=txnclose.env HG_HOOKTYPE=txnclose HG_NEW_OBSMARKERS=1 HG_TXNID=TXN:$ID$ HG_TXNNAME=debugobsolete
222 222 $ hg -R main bookmark --rev 32af7686d403 book_32af
223 223 pre-close-tip:02de42196ebe draft book_02de
224 224 postclose-tip:02de42196ebe draft book_02de
225 225 txnclose hook: HG_BOOKMARK_MOVED=1 HG_HOOKNAME=txnclose.env HG_HOOKTYPE=txnclose HG_TXNID=TXN:$ID$ HG_TXNNAME=bookmark
226 226 $ hg -R main debugobsolete -d '0 0' 7777777777777777777777777777777777777777 `getmainid 32af7686d403`
227 227 pre-close-tip:02de42196ebe draft book_02de
228 228 postclose-tip:02de42196ebe draft book_02de
229 229 txnclose hook: HG_HOOKNAME=txnclose.env HG_HOOKTYPE=txnclose HG_NEW_OBSMARKERS=1 HG_TXNID=TXN:$ID$ HG_TXNNAME=debugobsolete
230 230
231 231 $ hg -R other bookmark --rev cd010b8cd998 book_eea1
232 232 pre-close-tip:24b6387c8c8c public
233 233 postclose-tip:24b6387c8c8c public
234 234 txnclose hook: HG_BOOKMARK_MOVED=1 HG_HOOKNAME=txnclose.env HG_HOOKTYPE=txnclose HG_TXNID=TXN:$ID$ HG_TXNNAME=bookmark
235 235 $ hg -R other bookmark --rev cd010b8cd998 book_02de
236 236 pre-close-tip:24b6387c8c8c public
237 237 postclose-tip:24b6387c8c8c public
238 238 txnclose hook: HG_BOOKMARK_MOVED=1 HG_HOOKNAME=txnclose.env HG_HOOKTYPE=txnclose HG_TXNID=TXN:$ID$ HG_TXNNAME=bookmark
239 239 $ hg -R other bookmark --rev cd010b8cd998 book_42cc
240 240 pre-close-tip:24b6387c8c8c public
241 241 postclose-tip:24b6387c8c8c public
242 242 txnclose hook: HG_BOOKMARK_MOVED=1 HG_HOOKNAME=txnclose.env HG_HOOKTYPE=txnclose HG_TXNID=TXN:$ID$ HG_TXNNAME=bookmark
243 243 $ hg -R other bookmark --rev cd010b8cd998 book_5fdd
244 244 pre-close-tip:24b6387c8c8c public
245 245 postclose-tip:24b6387c8c8c public
246 246 txnclose hook: HG_BOOKMARK_MOVED=1 HG_HOOKNAME=txnclose.env HG_HOOKTYPE=txnclose HG_TXNID=TXN:$ID$ HG_TXNNAME=bookmark
247 247 $ hg -R other bookmark --rev cd010b8cd998 book_32af
248 248 pre-close-tip:24b6387c8c8c public
249 249 postclose-tip:24b6387c8c8c public
250 250 txnclose hook: HG_BOOKMARK_MOVED=1 HG_HOOKNAME=txnclose.env HG_HOOKTYPE=txnclose HG_TXNID=TXN:$ID$ HG_TXNNAME=bookmark
251 251
252 252 $ hg -R main phase --public eea13746799a
253 253 pre-close-tip:02de42196ebe draft book_02de
254 254 postclose-tip:02de42196ebe draft book_02de
255 255 txnclose hook: HG_HOOKNAME=txnclose.env HG_HOOKTYPE=txnclose HG_PHASES_MOVED=1 HG_TXNID=TXN:$ID$ HG_TXNNAME=phase
256 256
257 257 push
258 258 $ hg -R main push other --rev eea13746799a --bookmark book_eea1
259 259 pushing to other
260 260 searching for changes
261 261 remote: adding changesets
262 262 remote: adding manifests
263 263 remote: adding file changes
264 264 remote: added 1 changesets with 0 changes to 0 files (-1 heads)
265 265 remote: 1 new obsolescence markers
266 266 remote: pre-close-tip:eea13746799a public book_eea1
267 267 remote: pushkey: lock state after "bookmarks"
268 268 remote: lock: free
269 269 remote: wlock: free
270 270 remote: postclose-tip:eea13746799a public book_eea1
271 271 remote: txnclose hook: HG_BOOKMARK_MOVED=1 HG_BUNDLE2=1 HG_HOOKNAME=txnclose.env HG_HOOKTYPE=txnclose HG_NEW_OBSMARKERS=1 HG_NODE=eea13746799a9e0bfd88f29d3c2e9dc9389f524f HG_NODE_LAST=eea13746799a9e0bfd88f29d3c2e9dc9389f524f HG_PHASES_MOVED=1 HG_SOURCE=push HG_TXNID=TXN:$ID$ HG_TXNNAME=push HG_URL=file:$TESTTMP/other
272 272 updating bookmark book_eea1
273 273 pre-close-tip:02de42196ebe draft book_02de
274 274 postclose-tip:02de42196ebe draft book_02de
275 275 txnclose hook: HG_HOOKNAME=txnclose.env HG_HOOKTYPE=txnclose HG_SOURCE=push-response HG_TXNID=TXN:$ID$ HG_TXNNAME=push-response
276 276 file:/*/$TESTTMP/other HG_URL=file:$TESTTMP/other (glob)
277 277 $ hg -R other log -G
278 278 o 3:eea13746799a public Nicolas Dumazet <nicdumz.commits@gmail.com> book_eea1 G
279 279 |\
280 280 | o 2:24b6387c8c8c public Nicolas Dumazet <nicdumz.commits@gmail.com> F
281 281 | |
282 282 @ | 1:9520eea781bc public Nicolas Dumazet <nicdumz.commits@gmail.com> E
283 283 |/
284 284 o 0:cd010b8cd998 public Nicolas Dumazet <nicdumz.commits@gmail.com> book_02de book_32af book_42cc book_5fdd A
285 285
286 286 $ hg -R other debugobsolete
287 287 1111111111111111111111111111111111111111 9520eea781bcca16c1e15acc0ba14335a0e8e5ba 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
288 288 2222222222222222222222222222222222222222 24b6387c8c8cae37178880f3fa95ded3cb1cf785 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
289 289 3333333333333333333333333333333333333333 eea13746799a9e0bfd88f29d3c2e9dc9389f524f 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
290 290
291 291 pull over ssh
292 292
293 293 $ hg -R other pull ssh://user@dummy/main -r 02de42196ebe --bookmark book_02de
294 294 pulling from ssh://user@dummy/main
295 295 searching for changes
296 296 adding changesets
297 297 adding manifests
298 298 adding file changes
299 299 added 1 changesets with 1 changes to 1 files (+1 heads)
300 300 1 new obsolescence markers
301 301 updating bookmark book_02de
302 302 pre-close-tip:02de42196ebe draft book_02de
303 303 new changesets 02de42196ebe (1 drafts)
304 304 postclose-tip:02de42196ebe draft book_02de
305 305 txnclose hook: HG_BOOKMARK_MOVED=1 HG_HOOKNAME=txnclose.env HG_HOOKTYPE=txnclose HG_NEW_OBSMARKERS=1 HG_NODE=02de42196ebee42ef284b6780a87cdc96e8eaab6 HG_NODE_LAST=02de42196ebee42ef284b6780a87cdc96e8eaab6 HG_PHASES_MOVED=1 HG_SOURCE=pull HG_TXNID=TXN:$ID$ HG_TXNNAME=pull
306 306 ssh://user@dummy/main HG_URL=ssh://user@dummy/main
307 307 (run 'hg heads' to see heads, 'hg merge' to merge)
308 308 $ hg -R other debugobsolete
309 309 1111111111111111111111111111111111111111 9520eea781bcca16c1e15acc0ba14335a0e8e5ba 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
310 310 2222222222222222222222222222222222222222 24b6387c8c8cae37178880f3fa95ded3cb1cf785 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
311 311 3333333333333333333333333333333333333333 eea13746799a9e0bfd88f29d3c2e9dc9389f524f 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
312 312 4444444444444444444444444444444444444444 02de42196ebee42ef284b6780a87cdc96e8eaab6 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
313 313
314 314 pull over http
315 315
316 316 $ hg serve -R main -p $HGPORT -d --pid-file=main.pid -E main-error.log
317 317 $ cat main.pid >> $DAEMON_PIDS
318 318
319 319 $ hg -R other pull http://localhost:$HGPORT/ -r 42ccdea3bb16 --bookmark book_42cc
320 320 pulling from http://localhost:$HGPORT/
321 321 searching for changes
322 322 adding changesets
323 323 adding manifests
324 324 adding file changes
325 325 added 1 changesets with 1 changes to 1 files (+1 heads)
326 326 1 new obsolescence markers
327 327 updating bookmark book_42cc
328 328 pre-close-tip:42ccdea3bb16 draft book_42cc
329 329 new changesets 42ccdea3bb16 (1 drafts)
330 330 postclose-tip:42ccdea3bb16 draft book_42cc
331 331 txnclose hook: HG_BOOKMARK_MOVED=1 HG_HOOKNAME=txnclose.env HG_HOOKTYPE=txnclose HG_NEW_OBSMARKERS=1 HG_NODE=42ccdea3bb16d28e1848c95fe2e44c000f3f21b1 HG_NODE_LAST=42ccdea3bb16d28e1848c95fe2e44c000f3f21b1 HG_PHASES_MOVED=1 HG_SOURCE=pull HG_TXNID=TXN:$ID$ HG_TXNNAME=pull
332 332 http://localhost:$HGPORT/ HG_URL=http://localhost:$HGPORT/
333 333 (run 'hg heads .' to see heads, 'hg merge' to merge)
334 334 $ cat main-error.log
335 335 $ hg -R other debugobsolete
336 336 1111111111111111111111111111111111111111 9520eea781bcca16c1e15acc0ba14335a0e8e5ba 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
337 337 2222222222222222222222222222222222222222 24b6387c8c8cae37178880f3fa95ded3cb1cf785 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
338 338 3333333333333333333333333333333333333333 eea13746799a9e0bfd88f29d3c2e9dc9389f524f 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
339 339 4444444444444444444444444444444444444444 02de42196ebee42ef284b6780a87cdc96e8eaab6 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
340 340 5555555555555555555555555555555555555555 42ccdea3bb16d28e1848c95fe2e44c000f3f21b1 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
341 341
342 342 push over ssh
343 343
344 344 $ hg -R main push ssh://user@dummy/other -r 5fddd98957c8 --bookmark book_5fdd
345 345 pushing to ssh://user@dummy/other
346 346 searching for changes
347 347 remote: adding changesets
348 348 remote: adding manifests
349 349 remote: adding file changes
350 350 remote: added 1 changesets with 1 changes to 1 files
351 351 remote: 1 new obsolescence markers
352 352 remote: pre-close-tip:5fddd98957c8 draft book_5fdd
353 353 remote: pushkey: lock state after "bookmarks"
354 354 remote: lock: free
355 355 remote: wlock: free
356 356 remote: postclose-tip:5fddd98957c8 draft book_5fdd
357 357 remote: txnclose hook: HG_BOOKMARK_MOVED=1 HG_BUNDLE2=1 HG_HOOKNAME=txnclose.env HG_HOOKTYPE=txnclose HG_NEW_OBSMARKERS=1 HG_NODE=5fddd98957c8a54a4d436dfe1da9d87f21a1b97b HG_NODE_LAST=5fddd98957c8a54a4d436dfe1da9d87f21a1b97b HG_SOURCE=serve HG_TXNID=TXN:$ID$ HG_TXNNAME=serve HG_URL=remote:ssh:$LOCALIP
358 358 updating bookmark book_5fdd
359 359 pre-close-tip:02de42196ebe draft book_02de
360 360 postclose-tip:02de42196ebe draft book_02de
361 361 txnclose hook: HG_HOOKNAME=txnclose.env HG_HOOKTYPE=txnclose HG_SOURCE=push-response HG_TXNID=TXN:$ID$ HG_TXNNAME=push-response
362 362 ssh://user@dummy/other HG_URL=ssh://user@dummy/other
363 363 $ hg -R other log -G
364 364 o 6:5fddd98957c8 draft Nicolas Dumazet <nicdumz.commits@gmail.com> book_5fdd C
365 365 |
366 366 o 5:42ccdea3bb16 draft Nicolas Dumazet <nicdumz.commits@gmail.com> book_42cc B
367 367 |
368 368 | o 4:02de42196ebe draft Nicolas Dumazet <nicdumz.commits@gmail.com> book_02de H
369 369 | |
370 370 | | o 3:eea13746799a public Nicolas Dumazet <nicdumz.commits@gmail.com> book_eea1 G
371 371 | |/|
372 372 | o | 2:24b6387c8c8c public Nicolas Dumazet <nicdumz.commits@gmail.com> F
373 373 |/ /
374 374 | @ 1:9520eea781bc public Nicolas Dumazet <nicdumz.commits@gmail.com> E
375 375 |/
376 376 o 0:cd010b8cd998 public Nicolas Dumazet <nicdumz.commits@gmail.com> book_32af A
377 377
378 378 $ hg -R other debugobsolete
379 379 1111111111111111111111111111111111111111 9520eea781bcca16c1e15acc0ba14335a0e8e5ba 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
380 380 2222222222222222222222222222222222222222 24b6387c8c8cae37178880f3fa95ded3cb1cf785 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
381 381 3333333333333333333333333333333333333333 eea13746799a9e0bfd88f29d3c2e9dc9389f524f 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
382 382 4444444444444444444444444444444444444444 02de42196ebee42ef284b6780a87cdc96e8eaab6 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
383 383 5555555555555555555555555555555555555555 42ccdea3bb16d28e1848c95fe2e44c000f3f21b1 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
384 384 6666666666666666666666666666666666666666 5fddd98957c8a54a4d436dfe1da9d87f21a1b97b 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
385 385
386 386 push over http
387 387
388 388 $ hg serve -R other -p $HGPORT2 -d --pid-file=other.pid -E other-error.log
389 389 $ cat other.pid >> $DAEMON_PIDS
390 390
391 391 $ hg -R main phase --public 32af7686d403
392 392 pre-close-tip:02de42196ebe draft book_02de
393 393 postclose-tip:02de42196ebe draft book_02de
394 394 txnclose hook: HG_HOOKNAME=txnclose.env HG_HOOKTYPE=txnclose HG_PHASES_MOVED=1 HG_TXNID=TXN:$ID$ HG_TXNNAME=phase
395 395 $ hg -R main push http://localhost:$HGPORT2/ -r 32af7686d403 --bookmark book_32af
396 396 pushing to http://localhost:$HGPORT2/
397 397 searching for changes
398 398 remote: adding changesets
399 399 remote: adding manifests
400 400 remote: adding file changes
401 401 remote: added 1 changesets with 1 changes to 1 files
402 402 remote: 1 new obsolescence markers
403 403 remote: pre-close-tip:32af7686d403 public book_32af
404 404 remote: pushkey: lock state after "bookmarks"
405 405 remote: lock: free
406 406 remote: wlock: free
407 407 remote: postclose-tip:32af7686d403 public book_32af
408 408 remote: txnclose hook: HG_BOOKMARK_MOVED=1 HG_BUNDLE2=1 HG_HOOKNAME=txnclose.env HG_HOOKTYPE=txnclose HG_NEW_OBSMARKERS=1 HG_NODE=32af7686d403cf45b5d95f2d70cebea587ac806a HG_NODE_LAST=32af7686d403cf45b5d95f2d70cebea587ac806a HG_PHASES_MOVED=1 HG_SOURCE=serve HG_TXNID=TXN:$ID$ HG_TXNNAME=serve HG_URL=remote:http:$LOCALIP: (glob)
409 409 updating bookmark book_32af
410 410 pre-close-tip:02de42196ebe draft book_02de
411 411 postclose-tip:02de42196ebe draft book_02de
412 412 txnclose hook: HG_HOOKNAME=txnclose.env HG_HOOKTYPE=txnclose HG_SOURCE=push-response HG_TXNID=TXN:$ID$ HG_TXNNAME=push-response
413 413 http://localhost:$HGPORT2/ HG_URL=http://localhost:$HGPORT2/
414 414 $ cat other-error.log
415 415
416 416 Check final content.
417 417
418 418 $ hg -R other log -G
419 419 o 7:32af7686d403 public Nicolas Dumazet <nicdumz.commits@gmail.com> book_32af D
420 420 |
421 421 o 6:5fddd98957c8 public Nicolas Dumazet <nicdumz.commits@gmail.com> book_5fdd C
422 422 |
423 423 o 5:42ccdea3bb16 public Nicolas Dumazet <nicdumz.commits@gmail.com> book_42cc B
424 424 |
425 425 | o 4:02de42196ebe draft Nicolas Dumazet <nicdumz.commits@gmail.com> book_02de H
426 426 | |
427 427 | | o 3:eea13746799a public Nicolas Dumazet <nicdumz.commits@gmail.com> book_eea1 G
428 428 | |/|
429 429 | o | 2:24b6387c8c8c public Nicolas Dumazet <nicdumz.commits@gmail.com> F
430 430 |/ /
431 431 | @ 1:9520eea781bc public Nicolas Dumazet <nicdumz.commits@gmail.com> E
432 432 |/
433 433 o 0:cd010b8cd998 public Nicolas Dumazet <nicdumz.commits@gmail.com> A
434 434
435 435 $ hg -R other debugobsolete
436 436 1111111111111111111111111111111111111111 9520eea781bcca16c1e15acc0ba14335a0e8e5ba 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
437 437 2222222222222222222222222222222222222222 24b6387c8c8cae37178880f3fa95ded3cb1cf785 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
438 438 3333333333333333333333333333333333333333 eea13746799a9e0bfd88f29d3c2e9dc9389f524f 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
439 439 4444444444444444444444444444444444444444 02de42196ebee42ef284b6780a87cdc96e8eaab6 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
440 440 5555555555555555555555555555555555555555 42ccdea3bb16d28e1848c95fe2e44c000f3f21b1 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
441 441 6666666666666666666666666666666666666666 5fddd98957c8a54a4d436dfe1da9d87f21a1b97b 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
442 442 7777777777777777777777777777777777777777 32af7686d403cf45b5d95f2d70cebea587ac806a 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
443 443
444 444 (check that no 'pending' files remain)
445 445
446 446 $ ls -1 other/.hg/bookmarks*
447 447 other/.hg/bookmarks
448 448 $ ls -1 other/.hg/store/phaseroots*
449 449 other/.hg/store/phaseroots
450 450 $ ls -1 other/.hg/store/00changelog.i*
451 451 other/.hg/store/00changelog.i
452 452
453 453 Error Handling
454 454 ==============
455 455
456 456 Check that errors are properly returned to the client during push.
457 457
458 458 Setting up
459 459
460 460 $ cat > failpush.py << EOF
461 461 > """A small extension that makes push fails when using bundle2
462 462 >
463 463 > used to test error handling in bundle2
464 464 > """
465 465 >
466 466 > from mercurial import error
467 467 > from mercurial import bundle2
468 468 > from mercurial import exchange
469 469 > from mercurial import extensions
470 470 > from mercurial import registrar
471 471 > cmdtable = {}
472 472 > command = registrar.command(cmdtable)
473 473 >
474 474 > configtable = {}
475 475 > configitem = registrar.configitem(configtable)
476 476 > configitem(b'failpush', b'reason',
477 477 > default=None,
478 478 > )
479 479 >
480 480 > def _pushbundle2failpart(pushop, bundler):
481 481 > reason = pushop.ui.config(b'failpush', b'reason')
482 482 > part = None
483 483 > if reason == b'abort':
484 484 > bundler.newpart(b'test:abort')
485 485 > if reason == b'unknown':
486 486 > bundler.newpart(b'test:unknown')
487 487 > if reason == b'race':
488 488 > # 20 Bytes of crap
489 489 > bundler.newpart(b'check:heads', data=b'01234567890123456789')
490 490 >
491 491 > @bundle2.parthandler(b"test:abort")
492 492 > def handleabort(op, part):
493 493 > raise error.Abort(b'Abandon ship!', hint=b"don't panic")
494 494 >
495 495 > def uisetup(ui):
496 496 > exchange.b2partsgenmapping[b'failpart'] = _pushbundle2failpart
497 497 > exchange.b2partsgenorder.insert(0, b'failpart')
498 498 >
499 499 > EOF
500 500
501 501 $ cd main
502 502 $ hg up tip
503 503 3 files updated, 0 files merged, 1 files removed, 0 files unresolved
504 504 $ echo 'I' > I
505 505 $ hg add I
506 506 $ hg ci -m 'I'
507 507 pre-close-tip:e7ec4e813ba6 draft
508 508 postclose-tip:e7ec4e813ba6 draft
509 509 txnclose hook: HG_HOOKNAME=txnclose.env HG_HOOKTYPE=txnclose HG_TXNID=TXN:$ID$ HG_TXNNAME=commit
510 510 $ hg id
511 511 e7ec4e813ba6 tip
512 512 $ cd ..
513 513
514 514 $ cat << EOF >> $HGRCPATH
515 515 > [extensions]
516 516 > failpush=$TESTTMP/failpush.py
517 517 > EOF
518 518
519 519 $ killdaemons.py
520 520 $ hg serve -R other -p $HGPORT2 -d --pid-file=other.pid -E other-error.log
521 521 $ cat other.pid >> $DAEMON_PIDS
522 522
523 523 Doing the actual push: Abort error
524 524
525 525 $ cat << EOF >> $HGRCPATH
526 526 > [failpush]
527 527 > reason = abort
528 528 > EOF
529 529
530 530 $ hg -R main push other -r e7ec4e813ba6
531 531 pushing to other
532 532 searching for changes
533 533 abort: Abandon ship!
534 534 (don't panic)
535 535 [255]
536 536
537 537 $ hg -R main push ssh://user@dummy/other -r e7ec4e813ba6
538 538 pushing to ssh://user@dummy/other
539 539 searching for changes
540 540 remote: Abandon ship!
541 541 remote: (don't panic)
542 542 abort: push failed on remote
543 543 [255]
544 544
545 545 $ hg -R main push http://localhost:$HGPORT2/ -r e7ec4e813ba6
546 546 pushing to http://localhost:$HGPORT2/
547 547 searching for changes
548 548 remote: Abandon ship!
549 549 remote: (don't panic)
550 550 abort: push failed on remote
551 551 [255]
552 552
553 553
554 554 Doing the actual push: unknown mandatory parts
555 555
556 556 $ cat << EOF >> $HGRCPATH
557 557 > [failpush]
558 558 > reason = unknown
559 559 > EOF
560 560
561 561 $ hg -R main push other -r e7ec4e813ba6
562 562 pushing to other
563 563 searching for changes
564 564 abort: missing support for test:unknown
565 565 [255]
566 566
567 567 $ hg -R main push ssh://user@dummy/other -r e7ec4e813ba6
568 568 pushing to ssh://user@dummy/other
569 569 searching for changes
570 570 abort: missing support for test:unknown
571 571 [255]
572 572
573 573 $ hg -R main push http://localhost:$HGPORT2/ -r e7ec4e813ba6
574 574 pushing to http://localhost:$HGPORT2/
575 575 searching for changes
576 576 abort: missing support for test:unknown
577 577 [255]
578 578
579 579 Doing the actual push: race
580 580
581 581 $ cat << EOF >> $HGRCPATH
582 582 > [failpush]
583 583 > reason = race
584 584 > EOF
585 585
586 586 $ hg -R main push other -r e7ec4e813ba6
587 587 pushing to other
588 588 searching for changes
589 589 abort: push failed:
590 'repository changed while pushing - please try again'
590 'remote repository changed while pushing - please try again'
591 591 [255]
592 592
593 593 $ hg -R main push ssh://user@dummy/other -r e7ec4e813ba6
594 594 pushing to ssh://user@dummy/other
595 595 searching for changes
596 596 abort: push failed:
597 'repository changed while pushing - please try again'
597 'remote repository changed while pushing - please try again'
598 598 [255]
599 599
600 600 $ hg -R main push http://localhost:$HGPORT2/ -r e7ec4e813ba6
601 601 pushing to http://localhost:$HGPORT2/
602 602 searching for changes
603 603 abort: push failed:
604 'repository changed while pushing - please try again'
604 'remote repository changed while pushing - please try again'
605 605 [255]
606 606
607 607 Doing the actual push: hook abort
608 608
609 609 $ cat << EOF >> $HGRCPATH
610 610 > [failpush]
611 611 > reason =
612 612 > [hooks]
613 613 > pretxnclose.failpush = sh -c "echo 'You shall not pass!'; false"
614 614 > txnabort.failpush = sh -c "echo 'Cleaning up the mess...'"
615 615 > EOF
616 616
617 617 $ killdaemons.py
618 618 $ hg serve -R other -p $HGPORT2 -d --pid-file=other.pid -E other-error.log
619 619 $ cat other.pid >> $DAEMON_PIDS
620 620
621 621 $ hg -R main push other -r e7ec4e813ba6
622 622 pushing to other
623 623 searching for changes
624 624 remote: adding changesets
625 625 remote: adding manifests
626 626 remote: adding file changes
627 627 remote: added 1 changesets with 1 changes to 1 files
628 628 remote: pre-close-tip:e7ec4e813ba6 draft
629 629 remote: You shall not pass!
630 630 remote: transaction abort!
631 631 remote: Cleaning up the mess...
632 632 remote: rollback completed
633 633 abort: pretxnclose.failpush hook exited with status 1
634 634 [255]
635 635
636 636 $ hg -R main push ssh://user@dummy/other -r e7ec4e813ba6
637 637 pushing to ssh://user@dummy/other
638 638 searching for changes
639 639 remote: adding changesets
640 640 remote: adding manifests
641 641 remote: adding file changes
642 642 remote: added 1 changesets with 1 changes to 1 files
643 643 remote: pre-close-tip:e7ec4e813ba6 draft
644 644 remote: You shall not pass!
645 645 remote: transaction abort!
646 646 remote: Cleaning up the mess...
647 647 remote: rollback completed
648 648 remote: pretxnclose.failpush hook exited with status 1
649 649 abort: push failed on remote
650 650 [255]
651 651
652 652 $ hg -R main push http://localhost:$HGPORT2/ -r e7ec4e813ba6
653 653 pushing to http://localhost:$HGPORT2/
654 654 searching for changes
655 655 remote: adding changesets
656 656 remote: adding manifests
657 657 remote: adding file changes
658 658 remote: added 1 changesets with 1 changes to 1 files
659 659 remote: pre-close-tip:e7ec4e813ba6 draft
660 660 remote: You shall not pass!
661 661 remote: transaction abort!
662 662 remote: Cleaning up the mess...
663 663 remote: rollback completed
664 664 remote: pretxnclose.failpush hook exited with status 1
665 665 abort: push failed on remote
666 666 [255]
667 667
668 668 (check that no 'pending' files remain)
669 669
670 670 $ ls -1 other/.hg/bookmarks*
671 671 other/.hg/bookmarks
672 672 $ ls -1 other/.hg/store/phaseroots*
673 673 other/.hg/store/phaseroots
674 674 $ ls -1 other/.hg/store/00changelog.i*
675 675 other/.hg/store/00changelog.i
676 676
677 677 Check error from hook during the unbundling process itself
678 678
679 679 $ cat << EOF >> $HGRCPATH
680 680 > pretxnchangegroup = sh -c "echo 'Fail early!'; false"
681 681 > EOF
682 682 $ killdaemons.py # reload http config
683 683 $ hg serve -R other -p $HGPORT2 -d --pid-file=other.pid -E other-error.log
684 684 $ cat other.pid >> $DAEMON_PIDS
685 685
686 686 $ hg -R main push other -r e7ec4e813ba6
687 687 pushing to other
688 688 searching for changes
689 689 remote: adding changesets
690 690 remote: adding manifests
691 691 remote: adding file changes
692 692 remote: added 1 changesets with 1 changes to 1 files
693 693 remote: Fail early!
694 694 remote: transaction abort!
695 695 remote: Cleaning up the mess...
696 696 remote: rollback completed
697 697 abort: pretxnchangegroup hook exited with status 1
698 698 [255]
699 699 $ hg -R main push ssh://user@dummy/other -r e7ec4e813ba6
700 700 pushing to ssh://user@dummy/other
701 701 searching for changes
702 702 remote: adding changesets
703 703 remote: adding manifests
704 704 remote: adding file changes
705 705 remote: added 1 changesets with 1 changes to 1 files
706 706 remote: Fail early!
707 707 remote: transaction abort!
708 708 remote: Cleaning up the mess...
709 709 remote: rollback completed
710 710 remote: pretxnchangegroup hook exited with status 1
711 711 abort: push failed on remote
712 712 [255]
713 713 $ hg -R main push http://localhost:$HGPORT2/ -r e7ec4e813ba6
714 714 pushing to http://localhost:$HGPORT2/
715 715 searching for changes
716 716 remote: adding changesets
717 717 remote: adding manifests
718 718 remote: adding file changes
719 719 remote: added 1 changesets with 1 changes to 1 files
720 720 remote: Fail early!
721 721 remote: transaction abort!
722 722 remote: Cleaning up the mess...
723 723 remote: rollback completed
724 724 remote: pretxnchangegroup hook exited with status 1
725 725 abort: push failed on remote
726 726 [255]
727 727
728 728 Check output capture control.
729 729
730 730 (should be still forced for http, disabled for local and ssh)
731 731
732 732 $ cat >> $HGRCPATH << EOF
733 733 > [experimental]
734 734 > bundle2-output-capture=False
735 735 > EOF
736 736
737 737 $ hg -R main push other -r e7ec4e813ba6
738 738 pushing to other
739 739 searching for changes
740 740 adding changesets
741 741 adding manifests
742 742 adding file changes
743 743 added 1 changesets with 1 changes to 1 files
744 744 Fail early!
745 745 transaction abort!
746 746 Cleaning up the mess...
747 747 rollback completed
748 748 abort: pretxnchangegroup hook exited with status 1
749 749 [255]
750 750 $ hg -R main push ssh://user@dummy/other -r e7ec4e813ba6
751 751 pushing to ssh://user@dummy/other
752 752 searching for changes
753 753 remote: adding changesets
754 754 remote: adding manifests
755 755 remote: adding file changes
756 756 remote: added 1 changesets with 1 changes to 1 files
757 757 remote: Fail early!
758 758 remote: transaction abort!
759 759 remote: Cleaning up the mess...
760 760 remote: rollback completed
761 761 remote: pretxnchangegroup hook exited with status 1
762 762 abort: push failed on remote
763 763 [255]
764 764 $ hg -R main push http://localhost:$HGPORT2/ -r e7ec4e813ba6
765 765 pushing to http://localhost:$HGPORT2/
766 766 searching for changes
767 767 remote: adding changesets
768 768 remote: adding manifests
769 769 remote: adding file changes
770 770 remote: added 1 changesets with 1 changes to 1 files
771 771 remote: Fail early!
772 772 remote: transaction abort!
773 773 remote: Cleaning up the mess...
774 774 remote: rollback completed
775 775 remote: pretxnchangegroup hook exited with status 1
776 776 abort: push failed on remote
777 777 [255]
778 778
779 779 Check abort from mandatory pushkey
780 780
781 781 $ cat > mandatorypart.py << EOF
782 782 > from mercurial import exchange
783 783 > from mercurial import pushkey
784 784 > from mercurial import node
785 785 > from mercurial import error
786 786 > @exchange.b2partsgenerator(b'failingpuskey')
787 787 > def addfailingpushey(pushop, bundler):
788 788 > enc = pushkey.encode
789 789 > part = bundler.newpart(b'pushkey')
790 790 > part.addparam(b'namespace', enc(b'phases'))
791 791 > part.addparam(b'key', enc(b'cd010b8cd998f3981a5a8115f94f8da4ab506089'))
792 792 > part.addparam(b'old', enc(b'0')) # successful update
793 793 > part.addparam(b'new', enc(b'0'))
794 794 > def fail(pushop, exc):
795 795 > raise error.Abort(b'Correct phase push failed (because hooks)')
796 796 > pushop.pkfailcb[part.id] = fail
797 797 > EOF
798 798 $ cat >> $HGRCPATH << EOF
799 799 > [hooks]
800 800 > pretxnchangegroup=
801 801 > pretxnclose.failpush=
802 802 > prepushkey.failpush = sh -c "echo 'do not push the key !'; false"
803 803 > [extensions]
804 804 > mandatorypart=$TESTTMP/mandatorypart.py
805 805 > EOF
806 806 $ "$TESTDIR/killdaemons.py" $DAEMON_PIDS # reload http config
807 807 $ hg serve -R other -p $HGPORT2 -d --pid-file=other.pid -E other-error.log
808 808 $ cat other.pid >> $DAEMON_PIDS
809 809
810 810 (Failure from a hook)
811 811
812 812 $ hg -R main push other -r e7ec4e813ba6
813 813 pushing to other
814 814 searching for changes
815 815 adding changesets
816 816 adding manifests
817 817 adding file changes
818 818 added 1 changesets with 1 changes to 1 files
819 819 do not push the key !
820 820 pushkey-abort: prepushkey.failpush hook exited with status 1
821 821 transaction abort!
822 822 Cleaning up the mess...
823 823 rollback completed
824 824 abort: Correct phase push failed (because hooks)
825 825 [255]
826 826 $ hg -R main push ssh://user@dummy/other -r e7ec4e813ba6
827 827 pushing to ssh://user@dummy/other
828 828 searching for changes
829 829 remote: adding changesets
830 830 remote: adding manifests
831 831 remote: adding file changes
832 832 remote: added 1 changesets with 1 changes to 1 files
833 833 remote: do not push the key !
834 834 remote: pushkey-abort: prepushkey.failpush hook exited with status 1
835 835 remote: transaction abort!
836 836 remote: Cleaning up the mess...
837 837 remote: rollback completed
838 838 abort: Correct phase push failed (because hooks)
839 839 [255]
840 840 $ hg -R main push http://localhost:$HGPORT2/ -r e7ec4e813ba6
841 841 pushing to http://localhost:$HGPORT2/
842 842 searching for changes
843 843 remote: adding changesets
844 844 remote: adding manifests
845 845 remote: adding file changes
846 846 remote: added 1 changesets with 1 changes to 1 files
847 847 remote: do not push the key !
848 848 remote: pushkey-abort: prepushkey.failpush hook exited with status 1
849 849 remote: transaction abort!
850 850 remote: Cleaning up the mess...
851 851 remote: rollback completed
852 852 abort: Correct phase push failed (because hooks)
853 853 [255]
854 854
855 855 (Failure from a the pushkey)
856 856
857 857 $ cat > mandatorypart.py << EOF
858 858 > from mercurial import exchange
859 859 > from mercurial import pushkey
860 860 > from mercurial import node
861 861 > from mercurial import error
862 862 > @exchange.b2partsgenerator(b'failingpuskey')
863 863 > def addfailingpushey(pushop, bundler):
864 864 > enc = pushkey.encode
865 865 > part = bundler.newpart(b'pushkey')
866 866 > part.addparam(b'namespace', enc(b'phases'))
867 867 > part.addparam(b'key', enc(b'cd010b8cd998f3981a5a8115f94f8da4ab506089'))
868 868 > part.addparam(b'old', enc(b'4')) # will fail
869 869 > part.addparam(b'new', enc(b'3'))
870 870 > def fail(pushop, exc):
871 871 > raise error.Abort(b'Clown phase push failed')
872 872 > pushop.pkfailcb[part.id] = fail
873 873 > EOF
874 874 $ cat >> $HGRCPATH << EOF
875 875 > [hooks]
876 876 > prepushkey.failpush =
877 877 > EOF
878 878 $ "$TESTDIR/killdaemons.py" $DAEMON_PIDS # reload http config
879 879 $ hg serve -R other -p $HGPORT2 -d --pid-file=other.pid -E other-error.log
880 880 $ cat other.pid >> $DAEMON_PIDS
881 881
882 882 $ hg -R main push other -r e7ec4e813ba6
883 883 pushing to other
884 884 searching for changes
885 885 adding changesets
886 886 adding manifests
887 887 adding file changes
888 888 added 1 changesets with 1 changes to 1 files
889 889 transaction abort!
890 890 Cleaning up the mess...
891 891 rollback completed
892 892 pushkey: lock state after "phases"
893 893 lock: free
894 894 wlock: free
895 895 abort: Clown phase push failed
896 896 [255]
897 897 $ hg -R main push ssh://user@dummy/other -r e7ec4e813ba6
898 898 pushing to ssh://user@dummy/other
899 899 searching for changes
900 900 remote: adding changesets
901 901 remote: adding manifests
902 902 remote: adding file changes
903 903 remote: added 1 changesets with 1 changes to 1 files
904 904 remote: transaction abort!
905 905 remote: Cleaning up the mess...
906 906 remote: rollback completed
907 907 remote: pushkey: lock state after "phases"
908 908 remote: lock: free
909 909 remote: wlock: free
910 910 abort: Clown phase push failed
911 911 [255]
912 912 $ hg -R main push http://localhost:$HGPORT2/ -r e7ec4e813ba6
913 913 pushing to http://localhost:$HGPORT2/
914 914 searching for changes
915 915 remote: adding changesets
916 916 remote: adding manifests
917 917 remote: adding file changes
918 918 remote: added 1 changesets with 1 changes to 1 files
919 919 remote: transaction abort!
920 920 remote: Cleaning up the mess...
921 921 remote: rollback completed
922 922 remote: pushkey: lock state after "phases"
923 923 remote: lock: free
924 924 remote: wlock: free
925 925 abort: Clown phase push failed
926 926 [255]
927 927
928 928 Test lazily acquiring the lock during unbundle
929 929 $ cp $TESTTMP/hgrc.orig $HGRCPATH
930 930 $ cat >> $HGRCPATH <<EOF
931 931 > [ui]
932 932 > ssh="$PYTHON" "$TESTDIR/dummyssh"
933 933 > EOF
934 934
935 935 $ cat >> $TESTTMP/locktester.py <<EOF
936 936 > import os
937 937 > from mercurial import extensions, bundle2, error
938 938 > def checklock(orig, repo, *args, **kwargs):
939 939 > if repo.svfs.lexists(b"lock"):
940 940 > raise error.Abort(b"Lock should not be taken")
941 941 > return orig(repo, *args, **kwargs)
942 942 > def extsetup(ui):
943 943 > extensions.wrapfunction(bundle2, b'processbundle', checklock)
944 944 > EOF
945 945
946 946 $ hg init lazylock
947 947 $ cat >> lazylock/.hg/hgrc <<EOF
948 948 > [extensions]
949 949 > locktester=$TESTTMP/locktester.py
950 950 > EOF
951 951
952 952 $ hg clone -q ssh://user@dummy/lazylock lazylockclient
953 953 $ cd lazylockclient
954 954 $ touch a && hg ci -Aqm a
955 955 $ hg push
956 956 pushing to ssh://user@dummy/lazylock
957 957 searching for changes
958 958 remote: Lock should not be taken
959 959 abort: push failed on remote
960 960 [255]
961 961
962 962 $ cat >> ../lazylock/.hg/hgrc <<EOF
963 963 > [experimental]
964 964 > bundle2lazylocking=True
965 965 > EOF
966 966 $ hg push
967 967 pushing to ssh://user@dummy/lazylock
968 968 searching for changes
969 969 remote: adding changesets
970 970 remote: adding manifests
971 971 remote: adding file changes
972 972 remote: added 1 changesets with 1 changes to 1 files
973 973
974 974 $ cd ..
975 975
976 976 Servers can disable bundle1 for clone/pull operations
977 977
978 978 $ killdaemons.py
979 979 $ hg init bundle2onlyserver
980 980 $ cd bundle2onlyserver
981 981 $ cat > .hg/hgrc << EOF
982 982 > [server]
983 983 > bundle1.pull = false
984 984 > EOF
985 985
986 986 $ touch foo
987 987 $ hg -q commit -A -m initial
988 988
989 989 $ hg serve -p $HGPORT -d --pid-file=hg.pid
990 990 $ cat hg.pid >> $DAEMON_PIDS
991 991
992 992 $ hg --config devel.legacy.exchange=bundle1 clone http://localhost:$HGPORT/ not-bundle2
993 993 requesting all changes
994 994 abort: remote error:
995 995 incompatible Mercurial client; bundle2 required
996 996 (see https://www.mercurial-scm.org/wiki/IncompatibleClient)
997 997 [255]
998 998 $ killdaemons.py
999 999 $ cd ..
1000 1000
1001 1001 bundle1 can still pull non-generaldelta repos when generaldelta bundle1 disabled
1002 1002
1003 1003 $ hg --config format.usegeneraldelta=false init notgdserver
1004 1004 $ cd notgdserver
1005 1005 $ cat > .hg/hgrc << EOF
1006 1006 > [server]
1007 1007 > bundle1gd.pull = false
1008 1008 > EOF
1009 1009
1010 1010 $ touch foo
1011 1011 $ hg -q commit -A -m initial
1012 1012 $ hg serve -p $HGPORT -d --pid-file=hg.pid
1013 1013 $ cat hg.pid >> $DAEMON_PIDS
1014 1014
1015 1015 $ hg --config devel.legacy.exchange=bundle1 clone http://localhost:$HGPORT/ not-bundle2-1
1016 1016 requesting all changes
1017 1017 adding changesets
1018 1018 adding manifests
1019 1019 adding file changes
1020 1020 added 1 changesets with 1 changes to 1 files
1021 1021 new changesets 96ee1d7354c4
1022 1022 updating to branch default
1023 1023 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1024 1024
1025 1025 $ killdaemons.py
1026 1026 $ cd ../bundle2onlyserver
1027 1027
1028 1028 bundle1 pull can be disabled for generaldelta repos only
1029 1029
1030 1030 $ cat > .hg/hgrc << EOF
1031 1031 > [server]
1032 1032 > bundle1gd.pull = false
1033 1033 > EOF
1034 1034
1035 1035 $ hg serve -p $HGPORT -d --pid-file=hg.pid
1036 1036 $ cat hg.pid >> $DAEMON_PIDS
1037 1037 $ hg --config devel.legacy.exchange=bundle1 clone http://localhost:$HGPORT/ not-bundle2
1038 1038 requesting all changes
1039 1039 abort: remote error:
1040 1040 incompatible Mercurial client; bundle2 required
1041 1041 (see https://www.mercurial-scm.org/wiki/IncompatibleClient)
1042 1042 [255]
1043 1043
1044 1044 $ killdaemons.py
1045 1045
1046 1046 Verify the global server.bundle1 option works
1047 1047
1048 1048 $ cd ..
1049 1049 $ cat > bundle2onlyserver/.hg/hgrc << EOF
1050 1050 > [server]
1051 1051 > bundle1 = false
1052 1052 > EOF
1053 1053 $ hg serve -R bundle2onlyserver -p $HGPORT -d --pid-file=hg.pid
1054 1054 $ cat hg.pid >> $DAEMON_PIDS
1055 1055 $ hg --config devel.legacy.exchange=bundle1 clone http://localhost:$HGPORT not-bundle2
1056 1056 requesting all changes
1057 1057 abort: remote error:
1058 1058 incompatible Mercurial client; bundle2 required
1059 1059 (see https://www.mercurial-scm.org/wiki/IncompatibleClient)
1060 1060 [255]
1061 1061 $ killdaemons.py
1062 1062
1063 1063 $ hg --config devel.legacy.exchange=bundle1 clone ssh://user@dummy/bundle2onlyserver not-bundle2-ssh
1064 1064 requesting all changes
1065 1065 adding changesets
1066 1066 remote: abort: incompatible Mercurial client; bundle2 required
1067 1067 remote: (see https://www.mercurial-scm.org/wiki/IncompatibleClient)
1068 1068 transaction abort!
1069 1069 rollback completed
1070 1070 abort: stream ended unexpectedly (got 0 bytes, expected 4)
1071 1071 [255]
1072 1072
1073 1073 $ cat > bundle2onlyserver/.hg/hgrc << EOF
1074 1074 > [server]
1075 1075 > bundle1gd = false
1076 1076 > EOF
1077 1077 $ hg serve -R bundle2onlyserver -p $HGPORT -d --pid-file=hg.pid
1078 1078 $ cat hg.pid >> $DAEMON_PIDS
1079 1079
1080 1080 $ hg --config devel.legacy.exchange=bundle1 clone http://localhost:$HGPORT/ not-bundle2
1081 1081 requesting all changes
1082 1082 abort: remote error:
1083 1083 incompatible Mercurial client; bundle2 required
1084 1084 (see https://www.mercurial-scm.org/wiki/IncompatibleClient)
1085 1085 [255]
1086 1086
1087 1087 $ killdaemons.py
1088 1088
1089 1089 $ cd notgdserver
1090 1090 $ cat > .hg/hgrc << EOF
1091 1091 > [server]
1092 1092 > bundle1gd = false
1093 1093 > EOF
1094 1094 $ hg serve -p $HGPORT -d --pid-file=hg.pid
1095 1095 $ cat hg.pid >> $DAEMON_PIDS
1096 1096
1097 1097 $ hg --config devel.legacy.exchange=bundle1 clone http://localhost:$HGPORT/ not-bundle2-2
1098 1098 requesting all changes
1099 1099 adding changesets
1100 1100 adding manifests
1101 1101 adding file changes
1102 1102 added 1 changesets with 1 changes to 1 files
1103 1103 new changesets 96ee1d7354c4
1104 1104 updating to branch default
1105 1105 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1106 1106
1107 1107 $ killdaemons.py
1108 1108 $ cd ../bundle2onlyserver
1109 1109
1110 1110 Verify bundle1 pushes can be disabled
1111 1111
1112 1112 $ cat > .hg/hgrc << EOF
1113 1113 > [server]
1114 1114 > bundle1.push = false
1115 1115 > [web]
1116 1116 > allow_push = *
1117 1117 > push_ssl = false
1118 1118 > EOF
1119 1119
1120 1120 $ hg serve -p $HGPORT -d --pid-file=hg.pid -E error.log
1121 1121 $ cat hg.pid >> $DAEMON_PIDS
1122 1122 $ cd ..
1123 1123
1124 1124 $ hg clone http://localhost:$HGPORT bundle2-only
1125 1125 requesting all changes
1126 1126 adding changesets
1127 1127 adding manifests
1128 1128 adding file changes
1129 1129 added 1 changesets with 1 changes to 1 files
1130 1130 new changesets 96ee1d7354c4
1131 1131 updating to branch default
1132 1132 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1133 1133 $ cd bundle2-only
1134 1134 $ echo commit > foo
1135 1135 $ hg commit -m commit
1136 1136 $ hg --config devel.legacy.exchange=bundle1 push
1137 1137 pushing to http://localhost:$HGPORT/
1138 1138 searching for changes
1139 1139 abort: remote error:
1140 1140 incompatible Mercurial client; bundle2 required
1141 1141 (see https://www.mercurial-scm.org/wiki/IncompatibleClient)
1142 1142 [255]
1143 1143
1144 1144 (also check with ssh)
1145 1145
1146 1146 $ hg --config devel.legacy.exchange=bundle1 push ssh://user@dummy/bundle2onlyserver
1147 1147 pushing to ssh://user@dummy/bundle2onlyserver
1148 1148 searching for changes
1149 1149 remote: abort: incompatible Mercurial client; bundle2 required
1150 1150 remote: (see https://www.mercurial-scm.org/wiki/IncompatibleClient)
1151 1151 [1]
1152 1152
1153 1153 $ hg push
1154 1154 pushing to http://localhost:$HGPORT/
1155 1155 searching for changes
1156 1156 remote: adding changesets
1157 1157 remote: adding manifests
1158 1158 remote: adding file changes
1159 1159 remote: added 1 changesets with 1 changes to 1 files
@@ -1,1236 +1,1236
1 1 This test is dedicated to test the bundle2 container format
2 2
3 3 It test multiple existing parts to test different feature of the container. You
4 4 probably do not need to touch this test unless you change the binary encoding
5 5 of the bundle2 format itself.
6 6
7 7 Create an extension to test bundle2 API
8 8
9 9 $ cat > bundle2.py << EOF
10 10 > """A small extension to test bundle2 implementation
11 11 >
12 12 > This extension allows detailed testing of the various bundle2 API and
13 13 > behaviors.
14 14 > """
15 15 > import gc
16 16 > import os
17 17 > import sys
18 18 > from mercurial import util
19 19 > from mercurial import bundle2
20 20 > from mercurial import scmutil
21 21 > from mercurial import discovery
22 22 > from mercurial import changegroup
23 23 > from mercurial import error
24 24 > from mercurial import obsolete
25 25 > from mercurial import pycompat
26 26 > from mercurial import registrar
27 27 >
28 28 >
29 29 > try:
30 30 > import msvcrt
31 31 > msvcrt.setmode(sys.stdin.fileno(), os.O_BINARY)
32 32 > msvcrt.setmode(sys.stdout.fileno(), os.O_BINARY)
33 33 > msvcrt.setmode(sys.stderr.fileno(), os.O_BINARY)
34 34 > except ImportError:
35 35 > pass
36 36 >
37 37 > cmdtable = {}
38 38 > command = registrar.command(cmdtable)
39 39 >
40 40 > ELEPHANTSSONG = b"""Patali Dirapata, Cromda Cromda Ripalo, Pata Pata, Ko Ko Ko
41 41 > Bokoro Dipoulito, Rondi Rondi Pepino, Pata Pata, Ko Ko Ko
42 42 > Emana Karassoli, Loucra Loucra Ponponto, Pata Pata, Ko Ko Ko."""
43 43 > assert len(ELEPHANTSSONG) == 178 # future test say 178 bytes, trust it.
44 44 >
45 45 > @bundle2.parthandler(b'test:song')
46 46 > def songhandler(op, part):
47 47 > """handle a "test:song" bundle2 part, printing the lyrics on stdin"""
48 48 > op.ui.write(b'The choir starts singing:\n')
49 49 > verses = 0
50 50 > for line in part.read().split(b'\n'):
51 51 > op.ui.write(b' %s\n' % line)
52 52 > verses += 1
53 53 > op.records.add(b'song', {b'verses': verses})
54 54 >
55 55 > @bundle2.parthandler(b'test:ping')
56 56 > def pinghandler(op, part):
57 57 > op.ui.write(b'received ping request (id %i)\n' % part.id)
58 58 > if op.reply is not None and b'ping-pong' in op.reply.capabilities:
59 59 > op.ui.write_err(b'replying to ping request (id %i)\n' % part.id)
60 60 > op.reply.newpart(b'test:pong', [(b'in-reply-to', b'%d' % part.id)],
61 61 > mandatory=False)
62 62 >
63 63 > @bundle2.parthandler(b'test:debugreply')
64 64 > def debugreply(op, part):
65 65 > """print data about the capacity of the bundle reply"""
66 66 > if op.reply is None:
67 67 > op.ui.write(b'debugreply: no reply\n')
68 68 > else:
69 69 > op.ui.write(b'debugreply: capabilities:\n')
70 70 > for cap in sorted(op.reply.capabilities):
71 71 > op.ui.write(b"debugreply: '%s'\n" % cap)
72 72 > for val in op.reply.capabilities[cap]:
73 73 > op.ui.write(b"debugreply: '%s'\n" % val)
74 74 >
75 75 > @command(b'bundle2',
76 76 > [(b'', b'param', [], b'stream level parameter'),
77 77 > (b'', b'unknown', False, b'include an unknown mandatory part in the bundle'),
78 78 > (b'', b'unknownparams', False, b'include an unknown part parameters in the bundle'),
79 79 > (b'', b'parts', False, b'include some arbitrary parts to the bundle'),
80 80 > (b'', b'reply', False, b'produce a reply bundle'),
81 81 > (b'', b'pushrace', False, b'includes a check:head part with unknown nodes'),
82 82 > (b'', b'genraise', False, b'includes a part that raise an exception during generation'),
83 83 > (b'', b'timeout', False, b'emulate a timeout during bundle generation'),
84 84 > (b'r', b'rev', [], b'includes those changeset in the bundle'),
85 85 > (b'', b'compress', b'', b'compress the stream'),],
86 86 > b'[OUTPUTFILE]')
87 87 > def cmdbundle2(ui, repo, path=None, **opts):
88 88 > """write a bundle2 container on standard output"""
89 89 > bundler = bundle2.bundle20(ui)
90 90 > for p in opts['param']:
91 91 > p = p.split(b'=', 1)
92 92 > try:
93 93 > bundler.addparam(*p)
94 94 > except error.ProgrammingError as exc:
95 95 > raise error.Abort(b'%s' % exc)
96 96 >
97 97 > if opts['compress']:
98 98 > bundler.setcompression(opts['compress'])
99 99 >
100 100 > if opts['reply']:
101 101 > capsstring = b'ping-pong\nelephants=babar,celeste\ncity%3D%21=celeste%2Cville'
102 102 > bundler.newpart(b'replycaps', data=capsstring)
103 103 >
104 104 > if opts['pushrace']:
105 105 > # also serve to test the assignement of data outside of init
106 106 > part = bundler.newpart(b'check:heads')
107 107 > part.data = b'01234567890123456789'
108 108 >
109 109 > revs = opts['rev']
110 110 > if 'rev' in opts:
111 111 > revs = scmutil.revrange(repo, opts['rev'])
112 112 > if revs:
113 113 > # very crude version of a changegroup part creation
114 114 > bundled = repo.revs('%ld::%ld', revs, revs)
115 115 > headmissing = [c.node() for c in repo.set('heads(%ld)', revs)]
116 116 > headcommon = [c.node() for c in repo.set('parents(%ld) - %ld', revs, revs)]
117 117 > outgoing = discovery.outgoing(repo, headcommon, headmissing)
118 118 > cg = changegroup.makechangegroup(repo, outgoing, b'01',
119 119 > b'test:bundle2')
120 120 > bundler.newpart(b'changegroup', data=cg.getchunks(),
121 121 > mandatory=False)
122 122 >
123 123 > if opts['parts']:
124 124 > bundler.newpart(b'test:empty', mandatory=False)
125 125 > # add a second one to make sure we handle multiple parts
126 126 > bundler.newpart(b'test:empty', mandatory=False)
127 127 > bundler.newpart(b'test:song', data=ELEPHANTSSONG, mandatory=False)
128 128 > bundler.newpart(b'test:debugreply', mandatory=False)
129 129 > mathpart = bundler.newpart(b'test:math')
130 130 > mathpart.addparam(b'pi', b'3.14')
131 131 > mathpart.addparam(b'e', b'2.72')
132 132 > mathpart.addparam(b'cooking', b'raw', mandatory=False)
133 133 > mathpart.data = b'42'
134 134 > mathpart.mandatory = False
135 135 > # advisory known part with unknown mandatory param
136 136 > bundler.newpart(b'test:song', [(b'randomparam', b'')], mandatory=False)
137 137 > if opts['unknown']:
138 138 > bundler.newpart(b'test:unknown', data=b'some random content')
139 139 > if opts['unknownparams']:
140 140 > bundler.newpart(b'test:song', [(b'randomparams', b'')])
141 141 > if opts['parts']:
142 142 > bundler.newpart(b'test:ping', mandatory=False)
143 143 > if opts['genraise']:
144 144 > def genraise():
145 145 > yield b'first line\n'
146 146 > raise RuntimeError('Someone set up us the bomb!')
147 147 > bundler.newpart(b'output', data=genraise(), mandatory=False)
148 148 >
149 149 > if path is None:
150 150 > file = pycompat.stdout
151 151 > else:
152 152 > file = open(path, 'wb')
153 153 >
154 154 > if opts['timeout']:
155 155 > bundler.newpart(b'test:song', data=ELEPHANTSSONG, mandatory=False)
156 156 > for idx, junk in enumerate(bundler.getchunks()):
157 157 > ui.write(b'%d chunk\n' % idx)
158 158 > if idx > 4:
159 159 > # This throws a GeneratorExit inside the generator, which
160 160 > # can cause problems if the exception-recovery code is
161 161 > # too zealous. It's important for this test that the break
162 162 > # occur while we're in the middle of a part.
163 163 > break
164 164 > gc.collect()
165 165 > ui.write(b'fake timeout complete.\n')
166 166 > return
167 167 > try:
168 168 > for chunk in bundler.getchunks():
169 169 > file.write(chunk)
170 170 > except RuntimeError as exc:
171 171 > raise error.Abort(exc)
172 172 > finally:
173 173 > file.flush()
174 174 >
175 175 > @command(b'unbundle2', [], b'')
176 176 > def cmdunbundle2(ui, repo, replypath=None):
177 177 > """process a bundle2 stream from stdin on the current repo"""
178 178 > try:
179 179 > tr = None
180 180 > lock = repo.lock()
181 181 > tr = repo.transaction(b'processbundle')
182 182 > try:
183 183 > unbundler = bundle2.getunbundler(ui, pycompat.stdin)
184 184 > op = bundle2.processbundle(repo, unbundler, lambda: tr)
185 185 > tr.close()
186 186 > except error.BundleValueError as exc:
187 187 > raise error.Abort(b'missing support for %s' % exc)
188 188 > except error.PushRaced as exc:
189 189 > raise error.Abort(b'push race: %s' % exc)
190 190 > finally:
191 191 > if tr is not None:
192 192 > tr.release()
193 193 > lock.release()
194 194 > remains = pycompat.stdin.read()
195 195 > ui.write(b'%i unread bytes\n' % len(remains))
196 196 > if op.records[b'song']:
197 197 > totalverses = sum(r[b'verses'] for r in op.records[b'song'])
198 198 > ui.write(b'%i total verses sung\n' % totalverses)
199 199 > for rec in op.records[b'changegroup']:
200 200 > ui.write(b'addchangegroup return: %i\n' % rec[b'return'])
201 201 > if op.reply is not None and replypath is not None:
202 202 > with open(replypath, 'wb') as file:
203 203 > for chunk in op.reply.getchunks():
204 204 > file.write(chunk)
205 205 >
206 206 > @command(b'statbundle2', [], b'')
207 207 > def cmdstatbundle2(ui, repo):
208 208 > """print statistic on the bundle2 container read from stdin"""
209 209 > unbundler = bundle2.getunbundler(ui, pycompat.stdin)
210 210 > try:
211 211 > params = unbundler.params
212 212 > except error.BundleValueError as exc:
213 213 > raise error.Abort(b'unknown parameters: %s' % exc)
214 214 > ui.write(b'options count: %i\n' % len(params))
215 215 > for key in sorted(params):
216 216 > ui.write(b'- %s\n' % key)
217 217 > value = params[key]
218 218 > if value is not None:
219 219 > ui.write(b' %s\n' % value)
220 220 > count = 0
221 221 > for p in unbundler.iterparts():
222 222 > count += 1
223 223 > ui.write(b' :%s:\n' % p.type)
224 224 > ui.write(b' mandatory: %i\n' % len(p.mandatoryparams))
225 225 > ui.write(b' advisory: %i\n' % len(p.advisoryparams))
226 226 > ui.write(b' payload: %i bytes\n' % len(p.read()))
227 227 > ui.write(b'parts count: %i\n' % count)
228 228 > EOF
229 229 $ cat >> $HGRCPATH << EOF
230 230 > [extensions]
231 231 > bundle2=$TESTTMP/bundle2.py
232 232 > [experimental]
233 233 > evolution.createmarkers=True
234 234 > [ui]
235 235 > ssh="$PYTHON" "$TESTDIR/dummyssh"
236 236 > logtemplate={rev}:{node|short} {phase} {author} {bookmarks} {desc|firstline}
237 237 > [web]
238 238 > push_ssl = false
239 239 > allow_push = *
240 240 > [phases]
241 241 > publish=False
242 242 > EOF
243 243
244 244 The extension requires a repo (currently unused)
245 245
246 246 $ hg init main
247 247 $ cd main
248 248 $ touch a
249 249 $ hg add a
250 250 $ hg commit -m 'a'
251 251
252 252
253 253 Empty bundle
254 254 =================
255 255
256 256 - no option
257 257 - no parts
258 258
259 259 Test bundling
260 260
261 261 $ hg bundle2 | f --hexdump
262 262
263 263 0000: 48 47 32 30 00 00 00 00 00 00 00 00 |HG20........|
264 264
265 265 Test timeouts during bundling
266 266 $ hg bundle2 --timeout --debug --config devel.bundle2.debug=yes
267 267 bundle2-output-bundle: "HG20", 1 parts total
268 268 bundle2-output: start emission of HG20 stream
269 269 0 chunk
270 270 bundle2-output: bundle parameter:
271 271 1 chunk
272 272 bundle2-output: start of parts
273 273 bundle2-output: bundle part: "test:song"
274 274 bundle2-output-part: "test:song" (advisory) 178 bytes payload
275 275 bundle2-output: part 0: "test:song"
276 276 bundle2-output: header chunk size: 16
277 277 2 chunk
278 278 3 chunk
279 279 bundle2-output: payload chunk size: 178
280 280 4 chunk
281 281 5 chunk
282 282 bundle2-generatorexit
283 283 fake timeout complete.
284 284
285 285 Test unbundling
286 286
287 287 $ hg bundle2 | hg statbundle2
288 288 options count: 0
289 289 parts count: 0
290 290
291 291 Test old style bundle are detected and refused
292 292
293 293 $ hg bundle --all --type v1 ../bundle.hg
294 294 1 changesets found
295 295 $ hg statbundle2 < ../bundle.hg
296 296 abort: unknown bundle version 10
297 297 [255]
298 298
299 299 Test parameters
300 300 =================
301 301
302 302 - some options
303 303 - no parts
304 304
305 305 advisory parameters, no value
306 306 -------------------------------
307 307
308 308 Simplest possible parameters form
309 309
310 310 Test generation simple option
311 311
312 312 $ hg bundle2 --param 'caution' | f --hexdump
313 313
314 314 0000: 48 47 32 30 00 00 00 07 63 61 75 74 69 6f 6e 00 |HG20....caution.|
315 315 0010: 00 00 00 |...|
316 316
317 317 Test unbundling
318 318
319 319 $ hg bundle2 --param 'caution' | hg statbundle2
320 320 options count: 1
321 321 - caution
322 322 parts count: 0
323 323
324 324 Test generation multiple option
325 325
326 326 $ hg bundle2 --param 'caution' --param 'meal' | f --hexdump
327 327
328 328 0000: 48 47 32 30 00 00 00 0c 63 61 75 74 69 6f 6e 20 |HG20....caution |
329 329 0010: 6d 65 61 6c 00 00 00 00 |meal....|
330 330
331 331 Test unbundling
332 332
333 333 $ hg bundle2 --param 'caution' --param 'meal' | hg statbundle2
334 334 options count: 2
335 335 - caution
336 336 - meal
337 337 parts count: 0
338 338
339 339 advisory parameters, with value
340 340 -------------------------------
341 341
342 342 Test generation
343 343
344 344 $ hg bundle2 --param 'caution' --param 'meal=vegan' --param 'elephants' | f --hexdump
345 345
346 346 0000: 48 47 32 30 00 00 00 1c 63 61 75 74 69 6f 6e 20 |HG20....caution |
347 347 0010: 6d 65 61 6c 3d 76 65 67 61 6e 20 65 6c 65 70 68 |meal=vegan eleph|
348 348 0020: 61 6e 74 73 00 00 00 00 |ants....|
349 349
350 350 Test unbundling
351 351
352 352 $ hg bundle2 --param 'caution' --param 'meal=vegan' --param 'elephants' | hg statbundle2
353 353 options count: 3
354 354 - caution
355 355 - elephants
356 356 - meal
357 357 vegan
358 358 parts count: 0
359 359
360 360 parameter with special char in value
361 361 ---------------------------------------------------
362 362
363 363 Test generation
364 364
365 365 $ hg bundle2 --param 'e|! 7/=babar%#==tutu' --param simple | f --hexdump
366 366
367 367 0000: 48 47 32 30 00 00 00 29 65 25 37 43 25 32 31 25 |HG20...)e%7C%21%|
368 368 0010: 32 30 37 2f 3d 62 61 62 61 72 25 32 35 25 32 33 |207/=babar%25%23|
369 369 0020: 25 33 44 25 33 44 74 75 74 75 20 73 69 6d 70 6c |%3D%3Dtutu simpl|
370 370 0030: 65 00 00 00 00 |e....|
371 371
372 372 Test unbundling
373 373
374 374 $ hg bundle2 --param 'e|! 7/=babar%#==tutu' --param simple | hg statbundle2
375 375 options count: 2
376 376 - e|! 7/
377 377 babar%#==tutu
378 378 - simple
379 379 parts count: 0
380 380
381 381 Test unknown mandatory option
382 382 ---------------------------------------------------
383 383
384 384 $ hg bundle2 --param 'Gravity' | hg statbundle2
385 385 abort: unknown parameters: Stream Parameter - Gravity
386 386 [255]
387 387
388 388 Test debug output
389 389 ---------------------------------------------------
390 390
391 391 bundling debug
392 392
393 393 $ hg bundle2 --debug --param 'e|! 7/=babar%#==tutu' --param simple ../out.hg2 --config progress.debug=true --config devel.bundle2.debug=true
394 394 bundle2-output-bundle: "HG20", (2 params) 0 parts total
395 395 bundle2-output: start emission of HG20 stream
396 396 bundle2-output: bundle parameter: e%7C%21%207/=babar%25%23%3D%3Dtutu simple
397 397 bundle2-output: start of parts
398 398 bundle2-output: end of bundle
399 399
400 400 file content is ok
401 401
402 402 $ f --hexdump ../out.hg2
403 403 ../out.hg2:
404 404 0000: 48 47 32 30 00 00 00 29 65 25 37 43 25 32 31 25 |HG20...)e%7C%21%|
405 405 0010: 32 30 37 2f 3d 62 61 62 61 72 25 32 35 25 32 33 |207/=babar%25%23|
406 406 0020: 25 33 44 25 33 44 74 75 74 75 20 73 69 6d 70 6c |%3D%3Dtutu simpl|
407 407 0030: 65 00 00 00 00 |e....|
408 408
409 409 unbundling debug
410 410
411 411 $ hg statbundle2 --debug --config progress.debug=true --config devel.bundle2.debug=true < ../out.hg2
412 412 bundle2-input: start processing of HG20 stream
413 413 bundle2-input: reading bundle2 stream parameters
414 414 bundle2-input: ignoring unknown parameter e|! 7/
415 415 bundle2-input: ignoring unknown parameter simple
416 416 options count: 2
417 417 - e|! 7/
418 418 babar%#==tutu
419 419 - simple
420 420 bundle2-input: start extraction of bundle2 parts
421 421 bundle2-input: part header size: 0
422 422 bundle2-input: end of bundle2 stream
423 423 parts count: 0
424 424
425 425
426 426 Test buggy input
427 427 ---------------------------------------------------
428 428
429 429 empty parameter name
430 430
431 431 $ hg bundle2 --param '' --quiet
432 432 abort: empty parameter name
433 433 [255]
434 434
435 435 bad parameter name
436 436
437 437 $ hg bundle2 --param 42babar
438 438 abort: non letter first character: 42babar
439 439 [255]
440 440
441 441
442 442 Test part
443 443 =================
444 444
445 445 $ hg bundle2 --parts ../parts.hg2 --debug --config progress.debug=true --config devel.bundle2.debug=true
446 446 bundle2-output-bundle: "HG20", 7 parts total
447 447 bundle2-output: start emission of HG20 stream
448 448 bundle2-output: bundle parameter:
449 449 bundle2-output: start of parts
450 450 bundle2-output: bundle part: "test:empty"
451 451 bundle2-output-part: "test:empty" (advisory) empty payload
452 452 bundle2-output: part 0: "test:empty"
453 453 bundle2-output: header chunk size: 17
454 454 bundle2-output: closing payload chunk
455 455 bundle2-output: bundle part: "test:empty"
456 456 bundle2-output-part: "test:empty" (advisory) empty payload
457 457 bundle2-output: part 1: "test:empty"
458 458 bundle2-output: header chunk size: 17
459 459 bundle2-output: closing payload chunk
460 460 bundle2-output: bundle part: "test:song"
461 461 bundle2-output-part: "test:song" (advisory) 178 bytes payload
462 462 bundle2-output: part 2: "test:song"
463 463 bundle2-output: header chunk size: 16
464 464 bundle2-output: payload chunk size: 178
465 465 bundle2-output: closing payload chunk
466 466 bundle2-output: bundle part: "test:debugreply"
467 467 bundle2-output-part: "test:debugreply" (advisory) empty payload
468 468 bundle2-output: part 3: "test:debugreply"
469 469 bundle2-output: header chunk size: 22
470 470 bundle2-output: closing payload chunk
471 471 bundle2-output: bundle part: "test:math"
472 472 bundle2-output-part: "test:math" (advisory) (params: 2 mandatory 2 advisory) 2 bytes payload
473 473 bundle2-output: part 4: "test:math"
474 474 bundle2-output: header chunk size: 43
475 475 bundle2-output: payload chunk size: 2
476 476 bundle2-output: closing payload chunk
477 477 bundle2-output: bundle part: "test:song"
478 478 bundle2-output-part: "test:song" (advisory) (params: 1 mandatory) empty payload
479 479 bundle2-output: part 5: "test:song"
480 480 bundle2-output: header chunk size: 29
481 481 bundle2-output: closing payload chunk
482 482 bundle2-output: bundle part: "test:ping"
483 483 bundle2-output-part: "test:ping" (advisory) empty payload
484 484 bundle2-output: part 6: "test:ping"
485 485 bundle2-output: header chunk size: 16
486 486 bundle2-output: closing payload chunk
487 487 bundle2-output: end of bundle
488 488
489 489 $ f --hexdump ../parts.hg2
490 490 ../parts.hg2:
491 491 0000: 48 47 32 30 00 00 00 00 00 00 00 11 0a 74 65 73 |HG20.........tes|
492 492 0010: 74 3a 65 6d 70 74 79 00 00 00 00 00 00 00 00 00 |t:empty.........|
493 493 0020: 00 00 00 00 11 0a 74 65 73 74 3a 65 6d 70 74 79 |......test:empty|
494 494 0030: 00 00 00 01 00 00 00 00 00 00 00 00 00 10 09 74 |...............t|
495 495 0040: 65 73 74 3a 73 6f 6e 67 00 00 00 02 00 00 00 00 |est:song........|
496 496 0050: 00 b2 50 61 74 61 6c 69 20 44 69 72 61 70 61 74 |..Patali Dirapat|
497 497 0060: 61 2c 20 43 72 6f 6d 64 61 20 43 72 6f 6d 64 61 |a, Cromda Cromda|
498 498 0070: 20 52 69 70 61 6c 6f 2c 20 50 61 74 61 20 50 61 | Ripalo, Pata Pa|
499 499 0080: 74 61 2c 20 4b 6f 20 4b 6f 20 4b 6f 0a 42 6f 6b |ta, Ko Ko Ko.Bok|
500 500 0090: 6f 72 6f 20 44 69 70 6f 75 6c 69 74 6f 2c 20 52 |oro Dipoulito, R|
501 501 00a0: 6f 6e 64 69 20 52 6f 6e 64 69 20 50 65 70 69 6e |ondi Rondi Pepin|
502 502 00b0: 6f 2c 20 50 61 74 61 20 50 61 74 61 2c 20 4b 6f |o, Pata Pata, Ko|
503 503 00c0: 20 4b 6f 20 4b 6f 0a 45 6d 61 6e 61 20 4b 61 72 | Ko Ko.Emana Kar|
504 504 00d0: 61 73 73 6f 6c 69 2c 20 4c 6f 75 63 72 61 20 4c |assoli, Loucra L|
505 505 00e0: 6f 75 63 72 61 20 50 6f 6e 70 6f 6e 74 6f 2c 20 |oucra Ponponto, |
506 506 00f0: 50 61 74 61 20 50 61 74 61 2c 20 4b 6f 20 4b 6f |Pata Pata, Ko Ko|
507 507 0100: 20 4b 6f 2e 00 00 00 00 00 00 00 16 0f 74 65 73 | Ko..........tes|
508 508 0110: 74 3a 64 65 62 75 67 72 65 70 6c 79 00 00 00 03 |t:debugreply....|
509 509 0120: 00 00 00 00 00 00 00 00 00 2b 09 74 65 73 74 3a |.........+.test:|
510 510 0130: 6d 61 74 68 00 00 00 04 02 01 02 04 01 04 07 03 |math............|
511 511 0140: 70 69 33 2e 31 34 65 32 2e 37 32 63 6f 6f 6b 69 |pi3.14e2.72cooki|
512 512 0150: 6e 67 72 61 77 00 00 00 02 34 32 00 00 00 00 00 |ngraw....42.....|
513 513 0160: 00 00 1d 09 74 65 73 74 3a 73 6f 6e 67 00 00 00 |....test:song...|
514 514 0170: 05 01 00 0b 00 72 61 6e 64 6f 6d 70 61 72 61 6d |.....randomparam|
515 515 0180: 00 00 00 00 00 00 00 10 09 74 65 73 74 3a 70 69 |.........test:pi|
516 516 0190: 6e 67 00 00 00 06 00 00 00 00 00 00 00 00 00 00 |ng..............|
517 517
518 518
519 519 $ hg statbundle2 < ../parts.hg2
520 520 options count: 0
521 521 :test:empty:
522 522 mandatory: 0
523 523 advisory: 0
524 524 payload: 0 bytes
525 525 :test:empty:
526 526 mandatory: 0
527 527 advisory: 0
528 528 payload: 0 bytes
529 529 :test:song:
530 530 mandatory: 0
531 531 advisory: 0
532 532 payload: 178 bytes
533 533 :test:debugreply:
534 534 mandatory: 0
535 535 advisory: 0
536 536 payload: 0 bytes
537 537 :test:math:
538 538 mandatory: 2
539 539 advisory: 1
540 540 payload: 2 bytes
541 541 :test:song:
542 542 mandatory: 1
543 543 advisory: 0
544 544 payload: 0 bytes
545 545 :test:ping:
546 546 mandatory: 0
547 547 advisory: 0
548 548 payload: 0 bytes
549 549 parts count: 7
550 550
551 551 $ hg statbundle2 --debug --config progress.debug=true --config devel.bundle2.debug=true < ../parts.hg2
552 552 bundle2-input: start processing of HG20 stream
553 553 bundle2-input: reading bundle2 stream parameters
554 554 options count: 0
555 555 bundle2-input: start extraction of bundle2 parts
556 556 bundle2-input: part header size: 17
557 557 bundle2-input: part type: "test:empty"
558 558 bundle2-input: part id: "0"
559 559 bundle2-input: part parameters: 0
560 560 :test:empty:
561 561 mandatory: 0
562 562 advisory: 0
563 563 bundle2-input: payload chunk size: 0
564 564 payload: 0 bytes
565 565 bundle2-input: part header size: 17
566 566 bundle2-input: part type: "test:empty"
567 567 bundle2-input: part id: "1"
568 568 bundle2-input: part parameters: 0
569 569 :test:empty:
570 570 mandatory: 0
571 571 advisory: 0
572 572 bundle2-input: payload chunk size: 0
573 573 payload: 0 bytes
574 574 bundle2-input: part header size: 16
575 575 bundle2-input: part type: "test:song"
576 576 bundle2-input: part id: "2"
577 577 bundle2-input: part parameters: 0
578 578 :test:song:
579 579 mandatory: 0
580 580 advisory: 0
581 581 bundle2-input: payload chunk size: 178
582 582 bundle2-input: payload chunk size: 0
583 583 bundle2-input-part: total payload size 178
584 584 payload: 178 bytes
585 585 bundle2-input: part header size: 22
586 586 bundle2-input: part type: "test:debugreply"
587 587 bundle2-input: part id: "3"
588 588 bundle2-input: part parameters: 0
589 589 :test:debugreply:
590 590 mandatory: 0
591 591 advisory: 0
592 592 bundle2-input: payload chunk size: 0
593 593 payload: 0 bytes
594 594 bundle2-input: part header size: 43
595 595 bundle2-input: part type: "test:math"
596 596 bundle2-input: part id: "4"
597 597 bundle2-input: part parameters: 3
598 598 :test:math:
599 599 mandatory: 2
600 600 advisory: 1
601 601 bundle2-input: payload chunk size: 2
602 602 bundle2-input: payload chunk size: 0
603 603 bundle2-input-part: total payload size 2
604 604 payload: 2 bytes
605 605 bundle2-input: part header size: 29
606 606 bundle2-input: part type: "test:song"
607 607 bundle2-input: part id: "5"
608 608 bundle2-input: part parameters: 1
609 609 :test:song:
610 610 mandatory: 1
611 611 advisory: 0
612 612 bundle2-input: payload chunk size: 0
613 613 payload: 0 bytes
614 614 bundle2-input: part header size: 16
615 615 bundle2-input: part type: "test:ping"
616 616 bundle2-input: part id: "6"
617 617 bundle2-input: part parameters: 0
618 618 :test:ping:
619 619 mandatory: 0
620 620 advisory: 0
621 621 bundle2-input: payload chunk size: 0
622 622 payload: 0 bytes
623 623 bundle2-input: part header size: 0
624 624 bundle2-input: end of bundle2 stream
625 625 parts count: 7
626 626
627 627 Test actual unbundling of test part
628 628 =======================================
629 629
630 630 Process the bundle
631 631
632 632 $ hg unbundle2 --debug --config progress.debug=true --config devel.bundle2.debug=true < ../parts.hg2
633 633 bundle2-input: start processing of HG20 stream
634 634 bundle2-input: reading bundle2 stream parameters
635 635 bundle2-input-bundle: with-transaction
636 636 bundle2-input: start extraction of bundle2 parts
637 637 bundle2-input: part header size: 17
638 638 bundle2-input: part type: "test:empty"
639 639 bundle2-input: part id: "0"
640 640 bundle2-input: part parameters: 0
641 641 bundle2-input: ignoring unsupported advisory part test:empty
642 642 bundle2-input-part: "test:empty" (advisory) unsupported-type
643 643 bundle2-input: payload chunk size: 0
644 644 bundle2-input: part header size: 17
645 645 bundle2-input: part type: "test:empty"
646 646 bundle2-input: part id: "1"
647 647 bundle2-input: part parameters: 0
648 648 bundle2-input: ignoring unsupported advisory part test:empty
649 649 bundle2-input-part: "test:empty" (advisory) unsupported-type
650 650 bundle2-input: payload chunk size: 0
651 651 bundle2-input: part header size: 16
652 652 bundle2-input: part type: "test:song"
653 653 bundle2-input: part id: "2"
654 654 bundle2-input: part parameters: 0
655 655 bundle2-input: found a handler for part test:song
656 656 bundle2-input-part: "test:song" (advisory) supported
657 657 The choir starts singing:
658 658 bundle2-input: payload chunk size: 178
659 659 bundle2-input: payload chunk size: 0
660 660 bundle2-input-part: total payload size 178
661 661 Patali Dirapata, Cromda Cromda Ripalo, Pata Pata, Ko Ko Ko
662 662 Bokoro Dipoulito, Rondi Rondi Pepino, Pata Pata, Ko Ko Ko
663 663 Emana Karassoli, Loucra Loucra Ponponto, Pata Pata, Ko Ko Ko.
664 664 bundle2-input: part header size: 22
665 665 bundle2-input: part type: "test:debugreply"
666 666 bundle2-input: part id: "3"
667 667 bundle2-input: part parameters: 0
668 668 bundle2-input: found a handler for part test:debugreply
669 669 bundle2-input-part: "test:debugreply" (advisory) supported
670 670 debugreply: no reply
671 671 bundle2-input: payload chunk size: 0
672 672 bundle2-input: part header size: 43
673 673 bundle2-input: part type: "test:math"
674 674 bundle2-input: part id: "4"
675 675 bundle2-input: part parameters: 3
676 676 bundle2-input: ignoring unsupported advisory part test:math
677 677 bundle2-input-part: "test:math" (advisory) (params: 2 mandatory 2 advisory) unsupported-type
678 678 bundle2-input: payload chunk size: 2
679 679 bundle2-input: payload chunk size: 0
680 680 bundle2-input-part: total payload size 2
681 681 bundle2-input: part header size: 29
682 682 bundle2-input: part type: "test:song"
683 683 bundle2-input: part id: "5"
684 684 bundle2-input: part parameters: 1
685 685 bundle2-input: found a handler for part test:song
686 686 bundle2-input: ignoring unsupported advisory part test:song - randomparam
687 687 bundle2-input-part: "test:song" (advisory) (params: 1 mandatory) unsupported-params (randomparam)
688 688 bundle2-input: payload chunk size: 0
689 689 bundle2-input: part header size: 16
690 690 bundle2-input: part type: "test:ping"
691 691 bundle2-input: part id: "6"
692 692 bundle2-input: part parameters: 0
693 693 bundle2-input: found a handler for part test:ping
694 694 bundle2-input-part: "test:ping" (advisory) supported
695 695 received ping request (id 6)
696 696 bundle2-input: payload chunk size: 0
697 697 bundle2-input: part header size: 0
698 698 bundle2-input: end of bundle2 stream
699 699 bundle2-input-bundle: 6 parts total
700 700 0 unread bytes
701 701 3 total verses sung
702 702
703 703 Unbundle with an unknown mandatory part
704 704 (should abort)
705 705
706 706 $ hg bundle2 --parts --unknown ../unknown.hg2
707 707
708 708 $ hg unbundle2 < ../unknown.hg2
709 709 The choir starts singing:
710 710 Patali Dirapata, Cromda Cromda Ripalo, Pata Pata, Ko Ko Ko
711 711 Bokoro Dipoulito, Rondi Rondi Pepino, Pata Pata, Ko Ko Ko
712 712 Emana Karassoli, Loucra Loucra Ponponto, Pata Pata, Ko Ko Ko.
713 713 debugreply: no reply
714 714 0 unread bytes
715 715 abort: missing support for test:unknown
716 716 [255]
717 717
718 718 Unbundle with an unknown mandatory part parameters
719 719 (should abort)
720 720
721 721 $ hg bundle2 --unknownparams ../unknown.hg2
722 722
723 723 $ hg unbundle2 < ../unknown.hg2
724 724 0 unread bytes
725 725 abort: missing support for test:song - randomparams
726 726 [255]
727 727
728 728 unbundle with a reply
729 729
730 730 $ hg bundle2 --parts --reply ../parts-reply.hg2
731 731 $ hg unbundle2 ../reply.hg2 < ../parts-reply.hg2
732 732 0 unread bytes
733 733 3 total verses sung
734 734
735 735 The reply is a bundle
736 736
737 737 $ f --hexdump ../reply.hg2
738 738 ../reply.hg2:
739 739 0000: 48 47 32 30 00 00 00 00 00 00 00 1b 06 6f 75 74 |HG20.........out|
740 740 0010: 70 75 74 00 00 00 00 00 01 0b 01 69 6e 2d 72 65 |put........in-re|
741 741 0020: 70 6c 79 2d 74 6f 33 00 00 00 d9 54 68 65 20 63 |ply-to3....The c|
742 742 0030: 68 6f 69 72 20 73 74 61 72 74 73 20 73 69 6e 67 |hoir starts sing|
743 743 0040: 69 6e 67 3a 0a 20 20 20 20 50 61 74 61 6c 69 20 |ing:. Patali |
744 744 0050: 44 69 72 61 70 61 74 61 2c 20 43 72 6f 6d 64 61 |Dirapata, Cromda|
745 745 0060: 20 43 72 6f 6d 64 61 20 52 69 70 61 6c 6f 2c 20 | Cromda Ripalo, |
746 746 0070: 50 61 74 61 20 50 61 74 61 2c 20 4b 6f 20 4b 6f |Pata Pata, Ko Ko|
747 747 0080: 20 4b 6f 0a 20 20 20 20 42 6f 6b 6f 72 6f 20 44 | Ko. Bokoro D|
748 748 0090: 69 70 6f 75 6c 69 74 6f 2c 20 52 6f 6e 64 69 20 |ipoulito, Rondi |
749 749 00a0: 52 6f 6e 64 69 20 50 65 70 69 6e 6f 2c 20 50 61 |Rondi Pepino, Pa|
750 750 00b0: 74 61 20 50 61 74 61 2c 20 4b 6f 20 4b 6f 20 4b |ta Pata, Ko Ko K|
751 751 00c0: 6f 0a 20 20 20 20 45 6d 61 6e 61 20 4b 61 72 61 |o. Emana Kara|
752 752 00d0: 73 73 6f 6c 69 2c 20 4c 6f 75 63 72 61 20 4c 6f |ssoli, Loucra Lo|
753 753 00e0: 75 63 72 61 20 50 6f 6e 70 6f 6e 74 6f 2c 20 50 |ucra Ponponto, P|
754 754 00f0: 61 74 61 20 50 61 74 61 2c 20 4b 6f 20 4b 6f 20 |ata Pata, Ko Ko |
755 755 0100: 4b 6f 2e 0a 00 00 00 00 00 00 00 1b 06 6f 75 74 |Ko...........out|
756 756 0110: 70 75 74 00 00 00 01 00 01 0b 01 69 6e 2d 72 65 |put........in-re|
757 757 0120: 70 6c 79 2d 74 6f 34 00 00 00 c9 64 65 62 75 67 |ply-to4....debug|
758 758 0130: 72 65 70 6c 79 3a 20 63 61 70 61 62 69 6c 69 74 |reply: capabilit|
759 759 0140: 69 65 73 3a 0a 64 65 62 75 67 72 65 70 6c 79 3a |ies:.debugreply:|
760 760 0150: 20 20 20 20 20 27 63 69 74 79 3d 21 27 0a 64 65 | 'city=!'.de|
761 761 0160: 62 75 67 72 65 70 6c 79 3a 20 20 20 20 20 20 20 |bugreply: |
762 762 0170: 20 20 27 63 65 6c 65 73 74 65 2c 76 69 6c 6c 65 | 'celeste,ville|
763 763 0180: 27 0a 64 65 62 75 67 72 65 70 6c 79 3a 20 20 20 |'.debugreply: |
764 764 0190: 20 20 27 65 6c 65 70 68 61 6e 74 73 27 0a 64 65 | 'elephants'.de|
765 765 01a0: 62 75 67 72 65 70 6c 79 3a 20 20 20 20 20 20 20 |bugreply: |
766 766 01b0: 20 20 27 62 61 62 61 72 27 0a 64 65 62 75 67 72 | 'babar'.debugr|
767 767 01c0: 65 70 6c 79 3a 20 20 20 20 20 20 20 20 20 27 63 |eply: 'c|
768 768 01d0: 65 6c 65 73 74 65 27 0a 64 65 62 75 67 72 65 70 |eleste'.debugrep|
769 769 01e0: 6c 79 3a 20 20 20 20 20 27 70 69 6e 67 2d 70 6f |ly: 'ping-po|
770 770 01f0: 6e 67 27 0a 00 00 00 00 00 00 00 1e 09 74 65 73 |ng'..........tes|
771 771 0200: 74 3a 70 6f 6e 67 00 00 00 02 01 00 0b 01 69 6e |t:pong........in|
772 772 0210: 2d 72 65 70 6c 79 2d 74 6f 37 00 00 00 00 00 00 |-reply-to7......|
773 773 0220: 00 1b 06 6f 75 74 70 75 74 00 00 00 03 00 01 0b |...output.......|
774 774 0230: 01 69 6e 2d 72 65 70 6c 79 2d 74 6f 37 00 00 00 |.in-reply-to7...|
775 775 0240: 3d 72 65 63 65 69 76 65 64 20 70 69 6e 67 20 72 |=received ping r|
776 776 0250: 65 71 75 65 73 74 20 28 69 64 20 37 29 0a 72 65 |equest (id 7).re|
777 777 0260: 70 6c 79 69 6e 67 20 74 6f 20 70 69 6e 67 20 72 |plying to ping r|
778 778 0270: 65 71 75 65 73 74 20 28 69 64 20 37 29 0a 00 00 |equest (id 7)...|
779 779 0280: 00 00 00 00 00 00 |......|
780 780
781 781 The reply is valid
782 782
783 783 $ hg statbundle2 < ../reply.hg2
784 784 options count: 0
785 785 :output:
786 786 mandatory: 0
787 787 advisory: 1
788 788 payload: 217 bytes
789 789 :output:
790 790 mandatory: 0
791 791 advisory: 1
792 792 payload: 201 bytes
793 793 :test:pong:
794 794 mandatory: 1
795 795 advisory: 0
796 796 payload: 0 bytes
797 797 :output:
798 798 mandatory: 0
799 799 advisory: 1
800 800 payload: 61 bytes
801 801 parts count: 4
802 802
803 803 Unbundle the reply to get the output:
804 804
805 805 $ hg unbundle2 < ../reply.hg2
806 806 remote: The choir starts singing:
807 807 remote: Patali Dirapata, Cromda Cromda Ripalo, Pata Pata, Ko Ko Ko
808 808 remote: Bokoro Dipoulito, Rondi Rondi Pepino, Pata Pata, Ko Ko Ko
809 809 remote: Emana Karassoli, Loucra Loucra Ponponto, Pata Pata, Ko Ko Ko.
810 810 remote: debugreply: capabilities:
811 811 remote: debugreply: 'city=!'
812 812 remote: debugreply: 'celeste,ville'
813 813 remote: debugreply: 'elephants'
814 814 remote: debugreply: 'babar'
815 815 remote: debugreply: 'celeste'
816 816 remote: debugreply: 'ping-pong'
817 817 remote: received ping request (id 7)
818 818 remote: replying to ping request (id 7)
819 819 0 unread bytes
820 820
821 821 Test push race detection
822 822
823 823 $ hg bundle2 --pushrace ../part-race.hg2
824 824
825 825 $ hg unbundle2 < ../part-race.hg2
826 826 0 unread bytes
827 abort: push race: repository changed while pushing - please try again
827 abort: push race: remote repository changed while pushing - please try again
828 828 [255]
829 829
830 830 Support for changegroup
831 831 ===================================
832 832
833 833 $ hg unbundle $TESTDIR/bundles/rebase.hg
834 834 adding changesets
835 835 adding manifests
836 836 adding file changes
837 837 added 8 changesets with 7 changes to 7 files (+3 heads)
838 838 new changesets cd010b8cd998:02de42196ebe (8 drafts)
839 839 (run 'hg heads' to see heads, 'hg merge' to merge)
840 840
841 841 $ hg log -G
842 842 o 8:02de42196ebe draft Nicolas Dumazet <nicdumz.commits@gmail.com> H
843 843 |
844 844 | o 7:eea13746799a draft Nicolas Dumazet <nicdumz.commits@gmail.com> G
845 845 |/|
846 846 o | 6:24b6387c8c8c draft Nicolas Dumazet <nicdumz.commits@gmail.com> F
847 847 | |
848 848 | o 5:9520eea781bc draft Nicolas Dumazet <nicdumz.commits@gmail.com> E
849 849 |/
850 850 | o 4:32af7686d403 draft Nicolas Dumazet <nicdumz.commits@gmail.com> D
851 851 | |
852 852 | o 3:5fddd98957c8 draft Nicolas Dumazet <nicdumz.commits@gmail.com> C
853 853 | |
854 854 | o 2:42ccdea3bb16 draft Nicolas Dumazet <nicdumz.commits@gmail.com> B
855 855 |/
856 856 o 1:cd010b8cd998 draft Nicolas Dumazet <nicdumz.commits@gmail.com> A
857 857
858 858 @ 0:3903775176ed draft test a
859 859
860 860
861 861 $ hg bundle2 --debug --config progress.debug=true --config devel.bundle2.debug=true --rev '8+7+5+4' ../rev.hg2
862 862 4 changesets found
863 863 list of changesets:
864 864 32af7686d403cf45b5d95f2d70cebea587ac806a
865 865 9520eea781bcca16c1e15acc0ba14335a0e8e5ba
866 866 eea13746799a9e0bfd88f29d3c2e9dc9389f524f
867 867 02de42196ebee42ef284b6780a87cdc96e8eaab6
868 868 bundle2-output-bundle: "HG20", 1 parts total
869 869 bundle2-output: start emission of HG20 stream
870 870 bundle2-output: bundle parameter:
871 871 bundle2-output: start of parts
872 872 bundle2-output: bundle part: "changegroup"
873 873 bundle2-output-part: "changegroup" (advisory) streamed payload
874 874 bundle2-output: part 0: "changegroup"
875 875 bundle2-output: header chunk size: 18
876 876 changesets: 1/4 chunks (25.00%)
877 877 changesets: 2/4 chunks (50.00%)
878 878 changesets: 3/4 chunks (75.00%)
879 879 changesets: 4/4 chunks (100.00%)
880 880 manifests: 1/4 chunks (25.00%)
881 881 manifests: 2/4 chunks (50.00%)
882 882 manifests: 3/4 chunks (75.00%)
883 883 manifests: 4/4 chunks (100.00%)
884 884 files: D 1/3 files (33.33%)
885 885 files: E 2/3 files (66.67%)
886 886 files: H 3/3 files (100.00%)
887 887 bundle2-output: payload chunk size: 1555
888 888 bundle2-output: closing payload chunk
889 889 bundle2-output: end of bundle
890 890
891 891 $ f --hexdump ../rev.hg2
892 892 ../rev.hg2:
893 893 0000: 48 47 32 30 00 00 00 00 00 00 00 12 0b 63 68 61 |HG20.........cha|
894 894 0010: 6e 67 65 67 72 6f 75 70 00 00 00 00 00 00 00 00 |ngegroup........|
895 895 0020: 06 13 00 00 00 a4 32 af 76 86 d4 03 cf 45 b5 d9 |......2.v....E..|
896 896 0030: 5f 2d 70 ce be a5 87 ac 80 6a 5f dd d9 89 57 c8 |_-p......j_...W.|
897 897 0040: a5 4a 4d 43 6d fe 1d a9 d8 7f 21 a1 b9 7b 00 00 |.JMCm.....!..{..|
898 898 0050: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
899 899 0060: 00 00 32 af 76 86 d4 03 cf 45 b5 d9 5f 2d 70 ce |..2.v....E.._-p.|
900 900 0070: be a5 87 ac 80 6a 00 00 00 00 00 00 00 29 00 00 |.....j.......)..|
901 901 0080: 00 29 36 65 31 66 34 63 34 37 65 63 62 35 33 33 |.)6e1f4c47ecb533|
902 902 0090: 66 66 64 30 63 38 65 35 32 63 64 63 38 38 61 66 |ffd0c8e52cdc88af|
903 903 00a0: 62 36 63 64 33 39 65 32 30 63 0a 00 00 00 66 00 |b6cd39e20c....f.|
904 904 00b0: 00 00 68 00 00 00 02 44 0a 00 00 00 69 00 00 00 |..h....D....i...|
905 905 00c0: 6a 00 00 00 01 44 00 00 00 a4 95 20 ee a7 81 bc |j....D..... ....|
906 906 00d0: ca 16 c1 e1 5a cc 0b a1 43 35 a0 e8 e5 ba cd 01 |....Z...C5......|
907 907 00e0: 0b 8c d9 98 f3 98 1a 5a 81 15 f9 4f 8d a4 ab 50 |.......Z...O...P|
908 908 00f0: 60 89 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |`...............|
909 909 0100: 00 00 00 00 00 00 95 20 ee a7 81 bc ca 16 c1 e1 |....... ........|
910 910 0110: 5a cc 0b a1 43 35 a0 e8 e5 ba 00 00 00 00 00 00 |Z...C5..........|
911 911 0120: 00 29 00 00 00 29 34 64 65 63 65 39 63 38 32 36 |.)...)4dece9c826|
912 912 0130: 66 36 39 34 39 30 35 30 37 62 39 38 63 36 33 38 |f69490507b98c638|
913 913 0140: 33 61 33 30 30 39 62 32 39 35 38 33 37 64 0a 00 |3a3009b295837d..|
914 914 0150: 00 00 66 00 00 00 68 00 00 00 02 45 0a 00 00 00 |..f...h....E....|
915 915 0160: 69 00 00 00 6a 00 00 00 01 45 00 00 00 a2 ee a1 |i...j....E......|
916 916 0170: 37 46 79 9a 9e 0b fd 88 f2 9d 3c 2e 9d c9 38 9f |7Fy.......<...8.|
917 917 0180: 52 4f 24 b6 38 7c 8c 8c ae 37 17 88 80 f3 fa 95 |RO$.8|...7......|
918 918 0190: de d3 cb 1c f7 85 95 20 ee a7 81 bc ca 16 c1 e1 |....... ........|
919 919 01a0: 5a cc 0b a1 43 35 a0 e8 e5 ba ee a1 37 46 79 9a |Z...C5......7Fy.|
920 920 01b0: 9e 0b fd 88 f2 9d 3c 2e 9d c9 38 9f 52 4f 00 00 |......<...8.RO..|
921 921 01c0: 00 00 00 00 00 29 00 00 00 29 33 36 35 62 39 33 |.....)...)365b93|
922 922 01d0: 64 35 37 66 64 66 34 38 31 34 65 32 62 35 39 31 |d57fdf4814e2b591|
923 923 01e0: 31 64 36 62 61 63 66 66 32 62 31 32 30 31 34 34 |1d6bacff2b120144|
924 924 01f0: 34 31 0a 00 00 00 66 00 00 00 68 00 00 00 00 00 |41....f...h.....|
925 925 0200: 00 00 69 00 00 00 6a 00 00 00 01 47 00 00 00 a4 |..i...j....G....|
926 926 0210: 02 de 42 19 6e be e4 2e f2 84 b6 78 0a 87 cd c9 |..B.n......x....|
927 927 0220: 6e 8e aa b6 24 b6 38 7c 8c 8c ae 37 17 88 80 f3 |n...$.8|...7....|
928 928 0230: fa 95 de d3 cb 1c f7 85 00 00 00 00 00 00 00 00 |................|
929 929 0240: 00 00 00 00 00 00 00 00 00 00 00 00 02 de 42 19 |..............B.|
930 930 0250: 6e be e4 2e f2 84 b6 78 0a 87 cd c9 6e 8e aa b6 |n......x....n...|
931 931 0260: 00 00 00 00 00 00 00 29 00 00 00 29 38 62 65 65 |.......)...)8bee|
932 932 0270: 34 38 65 64 63 37 33 31 38 35 34 31 66 63 30 30 |48edc7318541fc00|
933 933 0280: 31 33 65 65 34 31 62 30 38 39 32 37 36 61 38 63 |13ee41b089276a8c|
934 934 0290: 32 34 62 66 0a 00 00 00 66 00 00 00 66 00 00 00 |24bf....f...f...|
935 935 02a0: 02 48 0a 00 00 00 67 00 00 00 68 00 00 00 01 48 |.H....g...h....H|
936 936 02b0: 00 00 00 00 00 00 00 8b 6e 1f 4c 47 ec b5 33 ff |........n.LG..3.|
937 937 02c0: d0 c8 e5 2c dc 88 af b6 cd 39 e2 0c 66 a5 a0 18 |...,.....9..f...|
938 938 02d0: 17 fd f5 23 9c 27 38 02 b5 b7 61 8d 05 1c 89 e4 |...#.'8...a.....|
939 939 02e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
940 940 02f0: 00 00 00 00 32 af 76 86 d4 03 cf 45 b5 d9 5f 2d |....2.v....E.._-|
941 941 0300: 70 ce be a5 87 ac 80 6a 00 00 00 81 00 00 00 81 |p......j........|
942 942 0310: 00 00 00 2b 44 00 63 33 66 31 63 61 32 39 32 34 |...+D.c3f1ca2924|
943 943 0320: 63 31 36 61 31 39 62 30 36 35 36 61 38 34 39 30 |c16a19b0656a8490|
944 944 0330: 30 65 35 30 34 65 35 62 30 61 65 63 32 64 0a 00 |0e504e5b0aec2d..|
945 945 0340: 00 00 8b 4d ec e9 c8 26 f6 94 90 50 7b 98 c6 38 |...M...&...P{..8|
946 946 0350: 3a 30 09 b2 95 83 7d 00 7d 8c 9d 88 84 13 25 f5 |:0....}.}.....%.|
947 947 0360: c6 b0 63 71 b3 5b 4e 8a 2b 1a 83 00 00 00 00 00 |..cq.[N.+.......|
948 948 0370: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 95 |................|
949 949 0380: 20 ee a7 81 bc ca 16 c1 e1 5a cc 0b a1 43 35 a0 | ........Z...C5.|
950 950 0390: e8 e5 ba 00 00 00 2b 00 00 00 ac 00 00 00 2b 45 |......+.......+E|
951 951 03a0: 00 39 63 36 66 64 30 33 35 30 61 36 63 30 64 30 |.9c6fd0350a6c0d0|
952 952 03b0: 63 34 39 64 34 61 39 63 35 30 31 37 63 66 30 37 |c49d4a9c5017cf07|
953 953 03c0: 30 34 33 66 35 34 65 35 38 0a 00 00 00 8b 36 5b |043f54e58.....6[|
954 954 03d0: 93 d5 7f df 48 14 e2 b5 91 1d 6b ac ff 2b 12 01 |....H.....k..+..|
955 955 03e0: 44 41 28 a5 84 c6 5e f1 21 f8 9e b6 6a b7 d0 bc |DA(...^.!...j...|
956 956 03f0: 15 3d 80 99 e7 ce 4d ec e9 c8 26 f6 94 90 50 7b |.=....M...&...P{|
957 957 0400: 98 c6 38 3a 30 09 b2 95 83 7d ee a1 37 46 79 9a |..8:0....}..7Fy.|
958 958 0410: 9e 0b fd 88 f2 9d 3c 2e 9d c9 38 9f 52 4f 00 00 |......<...8.RO..|
959 959 0420: 00 56 00 00 00 56 00 00 00 2b 46 00 32 32 62 66 |.V...V...+F.22bf|
960 960 0430: 63 66 64 36 32 61 32 31 61 33 32 38 37 65 64 62 |cfd62a21a3287edb|
961 961 0440: 64 34 64 36 35 36 32 31 38 64 30 66 35 32 35 65 |d4d656218d0f525e|
962 962 0450: 64 37 36 61 0a 00 00 00 97 8b ee 48 ed c7 31 85 |d76a.......H..1.|
963 963 0460: 41 fc 00 13 ee 41 b0 89 27 6a 8c 24 bf 28 a5 84 |A....A..'j.$.(..|
964 964 0470: c6 5e f1 21 f8 9e b6 6a b7 d0 bc 15 3d 80 99 e7 |.^.!...j....=...|
965 965 0480: ce 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
966 966 0490: 00 00 00 00 00 02 de 42 19 6e be e4 2e f2 84 b6 |.......B.n......|
967 967 04a0: 78 0a 87 cd c9 6e 8e aa b6 00 00 00 2b 00 00 00 |x....n......+...|
968 968 04b0: 56 00 00 00 00 00 00 00 81 00 00 00 81 00 00 00 |V...............|
969 969 04c0: 2b 48 00 38 35 30 30 31 38 39 65 37 34 61 39 65 |+H.8500189e74a9e|
970 970 04d0: 30 34 37 35 65 38 32 32 30 39 33 62 63 37 64 62 |0475e822093bc7db|
971 971 04e0: 30 64 36 33 31 61 65 62 30 62 34 0a 00 00 00 00 |0d631aeb0b4.....|
972 972 04f0: 00 00 00 05 44 00 00 00 62 c3 f1 ca 29 24 c1 6a |....D...b...)$.j|
973 973 0500: 19 b0 65 6a 84 90 0e 50 4e 5b 0a ec 2d 00 00 00 |..ej...PN[..-...|
974 974 0510: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
975 975 0520: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
976 976 0530: 00 00 00 00 00 32 af 76 86 d4 03 cf 45 b5 d9 5f |.....2.v....E.._|
977 977 0540: 2d 70 ce be a5 87 ac 80 6a 00 00 00 00 00 00 00 |-p......j.......|
978 978 0550: 00 00 00 00 02 44 0a 00 00 00 00 00 00 00 05 45 |.....D.........E|
979 979 0560: 00 00 00 62 9c 6f d0 35 0a 6c 0d 0c 49 d4 a9 c5 |...b.o.5.l..I...|
980 980 0570: 01 7c f0 70 43 f5 4e 58 00 00 00 00 00 00 00 00 |.|.pC.NX........|
981 981 0580: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
982 982 0590: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
983 983 05a0: 95 20 ee a7 81 bc ca 16 c1 e1 5a cc 0b a1 43 35 |. ........Z...C5|
984 984 05b0: a0 e8 e5 ba 00 00 00 00 00 00 00 00 00 00 00 02 |................|
985 985 05c0: 45 0a 00 00 00 00 00 00 00 05 48 00 00 00 62 85 |E.........H...b.|
986 986 05d0: 00 18 9e 74 a9 e0 47 5e 82 20 93 bc 7d b0 d6 31 |...t..G^. ..}..1|
987 987 05e0: ae b0 b4 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
988 988 05f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
989 989 0600: 00 00 00 00 00 00 00 00 00 00 00 02 de 42 19 6e |.............B.n|
990 990 0610: be e4 2e f2 84 b6 78 0a 87 cd c9 6e 8e aa b6 00 |......x....n....|
991 991 0620: 00 00 00 00 00 00 00 00 00 00 02 48 0a 00 00 00 |...........H....|
992 992 0630: 00 00 00 00 00 00 00 00 00 00 00 00 00 |.............|
993 993
994 994 $ hg debugbundle ../rev.hg2
995 995 Stream params: {}
996 996 changegroup -- {} (mandatory: False)
997 997 32af7686d403cf45b5d95f2d70cebea587ac806a
998 998 9520eea781bcca16c1e15acc0ba14335a0e8e5ba
999 999 eea13746799a9e0bfd88f29d3c2e9dc9389f524f
1000 1000 02de42196ebee42ef284b6780a87cdc96e8eaab6
1001 1001 $ hg unbundle ../rev.hg2
1002 1002 adding changesets
1003 1003 adding manifests
1004 1004 adding file changes
1005 1005 added 0 changesets with 0 changes to 3 files
1006 1006 (run 'hg update' to get a working copy)
1007 1007
1008 1008 with reply
1009 1009
1010 1010 $ hg bundle2 --rev '8+7+5+4' --reply ../rev-rr.hg2
1011 1011 $ hg unbundle2 ../rev-reply.hg2 < ../rev-rr.hg2
1012 1012 0 unread bytes
1013 1013 addchangegroup return: 1
1014 1014
1015 1015 $ f --hexdump ../rev-reply.hg2
1016 1016 ../rev-reply.hg2:
1017 1017 0000: 48 47 32 30 00 00 00 00 00 00 00 2f 11 72 65 70 |HG20......./.rep|
1018 1018 0010: 6c 79 3a 63 68 61 6e 67 65 67 72 6f 75 70 00 00 |ly:changegroup..|
1019 1019 0020: 00 00 00 02 0b 01 06 01 69 6e 2d 72 65 70 6c 79 |........in-reply|
1020 1020 0030: 2d 74 6f 31 72 65 74 75 72 6e 31 00 00 00 00 00 |-to1return1.....|
1021 1021 0040: 00 00 1b 06 6f 75 74 70 75 74 00 00 00 01 00 01 |....output......|
1022 1022 0050: 0b 01 69 6e 2d 72 65 70 6c 79 2d 74 6f 31 00 00 |..in-reply-to1..|
1023 1023 0060: 00 64 61 64 64 69 6e 67 20 63 68 61 6e 67 65 73 |.dadding changes|
1024 1024 0070: 65 74 73 0a 61 64 64 69 6e 67 20 6d 61 6e 69 66 |ets.adding manif|
1025 1025 0080: 65 73 74 73 0a 61 64 64 69 6e 67 20 66 69 6c 65 |ests.adding file|
1026 1026 0090: 20 63 68 61 6e 67 65 73 0a 61 64 64 65 64 20 30 | changes.added 0|
1027 1027 00a0: 20 63 68 61 6e 67 65 73 65 74 73 20 77 69 74 68 | changesets with|
1028 1028 00b0: 20 30 20 63 68 61 6e 67 65 73 20 74 6f 20 33 20 | 0 changes to 3 |
1029 1029 00c0: 66 69 6c 65 73 0a 00 00 00 00 00 00 00 00 |files.........|
1030 1030
1031 1031 Check handling of exception during generation.
1032 1032 ----------------------------------------------
1033 1033
1034 1034 $ hg bundle2 --genraise > ../genfailed.hg2
1035 1035 abort: Someone set up us the bomb!
1036 1036 [255]
1037 1037
1038 1038 Should still be a valid bundle
1039 1039
1040 1040 $ f --hexdump ../genfailed.hg2
1041 1041 ../genfailed.hg2:
1042 1042 0000: 48 47 32 30 00 00 00 00 00 00 00 0d 06 6f 75 74 |HG20.........out|
1043 1043 0010: 70 75 74 00 00 00 00 00 00 ff ff ff ff 00 00 00 |put.............|
1044 1044 0020: 48 0b 65 72 72 6f 72 3a 61 62 6f 72 74 00 00 00 |H.error:abort...|
1045 1045 0030: 00 01 00 07 2d 6d 65 73 73 61 67 65 75 6e 65 78 |....-messageunex|
1046 1046 0040: 70 65 63 74 65 64 20 65 72 72 6f 72 3a 20 53 6f |pected error: So|
1047 1047 0050: 6d 65 6f 6e 65 20 73 65 74 20 75 70 20 75 73 20 |meone set up us |
1048 1048 0060: 74 68 65 20 62 6f 6d 62 21 00 00 00 00 00 00 00 |the bomb!.......|
1049 1049 0070: 00 |.|
1050 1050
1051 1051 And its handling on the other size raise a clean exception
1052 1052
1053 1053 $ cat ../genfailed.hg2 | hg unbundle2
1054 1054 0 unread bytes
1055 1055 abort: unexpected error: Someone set up us the bomb!
1056 1056 [255]
1057 1057
1058 1058 Test compression
1059 1059 ================
1060 1060
1061 1061 Simple case where it just work: GZ
1062 1062 ----------------------------------
1063 1063
1064 1064 $ hg bundle2 --compress GZ --rev '8+7+5+4' ../rev.hg2.bz
1065 1065 $ f --hexdump ../rev.hg2.bz
1066 1066 ../rev.hg2.bz:
1067 1067 0000: 48 47 32 30 00 00 00 0e 43 6f 6d 70 72 65 73 73 |HG20....Compress|
1068 1068 0010: 69 6f 6e 3d 47 5a 78 9c 95 94 7d 68 95 55 1c c7 |ion=GZx...}h.U..|
1069 1069 0020: 9f 3b 31 e8 ce fa c3 65 be a0 a4 b4 52 b9 29 e7 |.;1....e....R.).|
1070 1070 0030: f5 79 ce 89 fa 63 ed 5e 77 8b 9c c3 3f 2a 1c 68 |.y...c.^w...?*.h|
1071 1071 0040: cf 79 9b dd 6a ae b0 28 74 b8 e5 96 5b bb 86 61 |.y..j..(t...[..a|
1072 1072 0050: a3 15 6e 3a 71 c8 6a e8 a5 da 95 64 28 22 ce 69 |..n:q.j....d(".i|
1073 1073 0060: cd 06 59 34 28 2b 51 2a 58 c3 17 56 2a 9a 9d 67 |..Y4(+Q*X..V*..g|
1074 1074 0070: dc c6 35 9e c4 1d f8 9e 87 f3 9c f3 3b bf 0f bf |..5.........;...|
1075 1075 0080: 97 e3 38 ce f4 42 b9 d6 af ae d2 55 af ae 7b ad |..8..B.....U..{.|
1076 1076 0090: c6 c9 8d bb 8a ec b4 07 ed 7f fd ed d3 53 be 4e |.............S.N|
1077 1077 00a0: f4 0e af 59 52 73 ea 50 d7 96 9e ba d4 9a 1f 87 |...YRs.P........|
1078 1078 00b0: 9b 9f 1d e8 7a 6a 79 e9 cb 7f cf eb fe 7e d3 82 |....zjy......~..|
1079 1079 00c0: ce 2f 36 38 21 23 cc 36 b7 b5 38 90 ab a1 21 92 |./68!#.6..8...!.|
1080 1080 00d0: 78 5a 0a 8a b1 31 0a 48 a6 29 92 4a 32 e6 1b e1 |xZ...1.H.).J2...|
1081 1081 00e0: 4a 85 b9 46 40 46 ed 61 63 b5 d6 aa 20 1e ac 5e |J..F@F.ac... ..^|
1082 1082 00f0: b0 0a ae 8a c4 03 c6 d6 f9 a3 7b eb fb 4e de 7f |..........{..N..|
1083 1083 0100: e4 97 55 5f 15 76 96 d2 5d bf 9d 3f 38 18 29 4c |..U_.v..]..?8.)L|
1084 1084 0110: 0f b7 5d 6e 9b b3 aa 7e c6 d5 15 5b f7 7c 52 f1 |..]n...~...[.|R.|
1085 1085 0120: 7c 73 18 63 98 6d 3e 23 51 5a 6a 2e 19 72 8d cb ||s.c.m>#QZj..r..|
1086 1086 0130: 09 07 14 78 82 33 e9 62 86 7d 0c 00 17 88 53 86 |...x.3.b.}....S.|
1087 1087 0140: 3d 75 0b 63 e2 16 c6 84 9d 76 8f 76 7a cb de fc |=u.c.....v.vz...|
1088 1088 0150: a8 a3 f0 46 d3 a5 f6 c7 96 b6 9f 60 3b 57 ae 28 |...F.......`;W.(|
1089 1089 0160: ce b2 8d e9 f4 3e 6f 66 53 dd e5 6b ad 67 be f9 |.....>ofS..k.g..|
1090 1090 0170: 72 ee 5f 8d 61 3c 61 b6 f9 8c d8 a5 82 63 45 3d |r._.a<a......cE=|
1091 1091 0180: a3 0c 61 90 68 24 28 87 50 b9 c2 97 c6 20 01 11 |..a.h$(.P.... ..|
1092 1092 0190: 80 84 10 98 cf e8 e4 13 96 05 51 2c 38 f3 c4 ec |..........Q,8...|
1093 1093 01a0: ea 43 e7 96 5e 6a c8 be 11 dd 32 78 a2 fa dd 8f |.C..^j....2x....|
1094 1094 01b0: b3 61 84 61 51 0c b3 cd 27 64 42 6b c2 b4 92 1e |.a.aQ...'dBk....|
1095 1095 01c0: 86 8c 12 68 24 00 10 db 7f 50 00 c6 91 e7 fa 4c |...h$....P.....L|
1096 1096 01d0: 22 22 cc bf 84 81 0a 92 c1 aa 2a c7 1b 49 e6 ee |""........*..I..|
1097 1097 01e0: 6b a9 7e e0 e9 b2 91 5e 7c 73 68 e0 fc 23 3f 34 |k.~....^|sh..#?4|
1098 1098 01f0: ed cf 0e f2 b3 d3 4c d7 ae 59 33 6f 8c 3d b8 63 |......L..Y3o.=.c|
1099 1099 0200: 21 2b e8 3d e0 6f 9d 3a b7 f9 dc 24 2a b2 3e a7 |!+.=.o.:...$*.>.|
1100 1100 0210: 58 dc 91 d8 40 e9 23 8e 88 84 ae 0f b9 00 2e b5 |X...@.#.........|
1101 1101 0220: 74 36 f3 40 53 40 34 15 c0 d7 12 8d e7 bb 65 f9 |t6.@S@4.......e.|
1102 1102 0230: c8 ef 03 0f ff f9 fe b6 8a 0d 6d fd ec 51 70 f7 |..........m..Qp.|
1103 1103 0240: a7 ad 9b 6b 9d da 74 7b 53 43 d1 43 63 fd 19 f9 |...k..t{SC.Cc...|
1104 1104 0250: ca 67 95 e5 ef c4 e6 6c 9e 44 e1 c5 ac 7a 82 6f |.g.....l.D...z.o|
1105 1105 0260: c2 e1 d2 b5 2d 81 29 f0 5d 09 6c 6f 10 ae 88 cf |....-.).].lo....|
1106 1106 0270: 25 05 d0 93 06 78 80 60 43 2d 10 1b 47 71 2b b7 |%....x.`C-..Gq+.|
1107 1107 0280: 7f bb e9 a7 e4 7d 67 7b df 9b f7 62 cf cd d8 f4 |.....}g{...b....|
1108 1108 0290: 48 bc 64 51 57 43 ff ea 8b 0b ae 74 64 53 07 86 |H.dQWC.....tdS..|
1109 1109 02a0: fa 66 3c 5e f7 e1 af a7 c2 90 ff a7 be 9e c9 29 |.f<^...........)|
1110 1110 02b0: b6 cc 41 48 18 69 94 8b 7c 04 7d 8c 98 a7 95 50 |..AH.i..|.}....P|
1111 1111 02c0: 44 d9 d0 20 c8 14 30 14 51 ad 6c 16 03 94 0f 5a |D.. ..0.Q.l....Z|
1112 1112 02d0: 46 93 7f 1c 87 8d 25 d7 9d a2 d1 92 4c f3 c2 54 |F.....%.....L..T|
1113 1113 02e0: ba f8 70 18 ca 24 0a 29 96 43 71 f2 93 95 74 18 |..p..$.).Cq...t.|
1114 1114 02f0: b5 65 c4 b8 f6 6c 5c 34 20 1e d5 0c 21 c0 b1 90 |.e...l\4 ...!...|
1115 1115 0300: 9e 12 40 b9 18 fa 5a 00 41 a2 39 d3 a9 c1 73 21 |..@...Z.A.9...s!|
1116 1116 0310: 8e 5e 3c b9 b8 f8 48 6a 76 46 a7 1a b6 dd 5b 51 |.^<...HjvF....[Q|
1117 1117 0320: 5e 19 1d 59 12 c6 32 89 02 9a c0 8f 4f b8 0a ba |^..Y..2.....O...|
1118 1118 0330: 5e ec 58 37 44 a3 2f dd 33 ed c9 d3 dd c7 22 1b |^.X7D./.3.....".|
1119 1119 0340: 2f d4 94 8e 95 3f 77 a7 ae 6e f3 32 8d bb 4a 4c |/....?w..n.2..JL|
1120 1120 0350: b8 0a 5a 43 34 3a b3 3a d6 77 ff 5c b6 fa ad f9 |..ZC4:.:.w.\....|
1121 1121 0360: db fb 6a 33 df c1 7d 99 cf ef d4 d5 6d da 77 7c |..j3..}.....m.w||
1122 1122 0370: 3b 19 fd af c5 3f f1 60 c3 17 |;....?.`..|
1123 1123 $ hg debugbundle ../rev.hg2.bz
1124 1124 Stream params: {Compression: GZ}
1125 1125 changegroup -- {} (mandatory: False)
1126 1126 32af7686d403cf45b5d95f2d70cebea587ac806a
1127 1127 9520eea781bcca16c1e15acc0ba14335a0e8e5ba
1128 1128 eea13746799a9e0bfd88f29d3c2e9dc9389f524f
1129 1129 02de42196ebee42ef284b6780a87cdc96e8eaab6
1130 1130 $ hg unbundle ../rev.hg2.bz
1131 1131 adding changesets
1132 1132 adding manifests
1133 1133 adding file changes
1134 1134 added 0 changesets with 0 changes to 3 files
1135 1135 (run 'hg update' to get a working copy)
1136 1136 Simple case where it just work: BZ
1137 1137 ----------------------------------
1138 1138
1139 1139 $ hg bundle2 --compress BZ --rev '8+7+5+4' ../rev.hg2.bz
1140 1140 $ f --hexdump ../rev.hg2.bz
1141 1141 ../rev.hg2.bz:
1142 1142 0000: 48 47 32 30 00 00 00 0e 43 6f 6d 70 72 65 73 73 |HG20....Compress|
1143 1143 0010: 69 6f 6e 3d 42 5a 42 5a 68 39 31 41 59 26 53 59 |ion=BZBZh91AY&SY|
1144 1144 0020: a3 4b 18 3d 00 00 1a 7f ff ff bf 5f f6 ef ef 7f |.K.=......._....|
1145 1145 0030: f6 3f f7 d1 d9 ff ff f7 6e ff ff 6e f7 f6 bd df |.?......n..n....|
1146 1146 0040: b5 ab ff cf 67 f6 e7 7b f7 c0 02 d7 33 82 8b 51 |....g..{....3..Q|
1147 1147 0050: 04 a5 53 d5 3d 27 a0 99 18 4d 0d 34 00 d1 a1 e8 |..S.='...M.4....|
1148 1148 0060: 80 c8 7a 87 a9 a3 43 6a 3d 46 86 26 80 34 3d 40 |..z...Cj=F.&.4=@|
1149 1149 0070: c8 c9 b5 34 f4 8f 48 0f 51 ea 34 34 fd 4d aa 19 |...4..H.Q.44.M..|
1150 1150 0080: 03 40 0c 08 da 86 43 d4 f5 0f 42 1e a0 f3 54 33 |.@....C...B...T3|
1151 1151 0090: 54 d3 13 4d 03 40 32 00 00 32 03 26 80 0d 00 0d |T..M.@2..2.&....|
1152 1152 00a0: 00 68 c8 c8 03 20 32 30 98 8c 80 00 00 03 4d 00 |.h... 20......M.|
1153 1153 00b0: c8 00 00 0d 00 00 22 99 a1 34 c2 64 a6 d5 34 1a |......"..4.d..4.|
1154 1154 00c0: 00 00 06 86 83 4d 07 a8 d1 a0 68 01 a0 00 00 00 |.....M....h.....|
1155 1155 00d0: 00 0d 06 80 00 00 00 0d 00 03 40 00 00 04 a4 a1 |..........@.....|
1156 1156 00e0: 4d a9 89 89 b4 9a 32 0c 43 46 86 87 a9 8d 41 9a |M.....2.CF....A.|
1157 1157 00f0: 98 46 9a 0d 31 32 1a 34 0d 0c 8d a2 0c 98 4d 06 |.F..12.4......M.|
1158 1158 0100: 8c 40 c2 60 8d 0d 0c 20 c9 89 fa a0 d0 d3 21 a1 |.@.`... ......!.|
1159 1159 0110: ea 34 d3 68 9e a6 d1 74 05 33 cb 66 96 93 28 64 |.4.h...t.3.f..(d|
1160 1160 0120: 40 91 22 ac 55 9b ea 40 7b 38 94 e2 f8 06 00 cb |@.".U..@{8......|
1161 1161 0130: 28 02 00 4d ab 40 24 10 43 18 cf 64 b4 06 83 0c |(..M.@$.C..d....|
1162 1162 0140: 34 6c b4 a3 d4 0a 0a e4 a8 5c 4e 23 c0 c9 7a 31 |4l.......\N#..z1|
1163 1163 0150: 97 87 77 7a 64 88 80 8e 60 97 20 93 0f 8e eb c4 |..wzd...`. .....|
1164 1164 0160: 62 a4 44 a3 52 20 b2 99 a9 2e e1 d7 29 4a 54 ac |b.D.R ......)JT.|
1165 1165 0170: 44 7a bb cc 04 3d e0 aa bd 6a 33 5e 9b a2 57 36 |Dz...=...j3^..W6|
1166 1166 0180: fa cb 45 bb 6d 3e c1 d9 d9 f5 83 69 8a d0 e0 e2 |..E.m>.....i....|
1167 1167 0190: e7 ae 90 55 24 da 3f ab 78 c0 4c b4 56 a3 9e a4 |...U$.?.x.L.V...|
1168 1168 01a0: af 9c 65 74 86 ec 6d dc 62 dc 33 ca c8 50 dd 9d |..et..m.b.3..P..|
1169 1169 01b0: 98 8e 9e 59 20 f3 f0 42 91 4a 09 f5 75 8d 3d a5 |...Y ..B.J..u.=.|
1170 1170 01c0: a5 15 cb 8d 10 63 b0 c2 2e b2 81 f7 c1 76 0e 53 |.....c.......v.S|
1171 1171 01d0: 6c 0e 46 73 b5 ae 67 f9 4c 0b 45 6b a8 32 2a 2f |l.Fs..g.L.Ek.2*/|
1172 1172 01e0: a2 54 a4 44 05 20 a1 38 d1 a4 c6 09 a8 2b 08 99 |.T.D. .8.....+..|
1173 1173 01f0: a4 14 ae 8d a3 e3 aa 34 27 d8 44 ca c3 5d 21 8b |.......4'.D..]!.|
1174 1174 0200: 1a 1e 97 29 71 2b 09 4a 4a 55 55 94 58 65 b2 bc |...)q+.JJUU.Xe..|
1175 1175 0210: f3 a5 90 26 36 76 67 7a 51 98 d6 8a 4a 99 50 b5 |...&6vgzQ...J.P.|
1176 1176 0220: 99 8f 94 21 17 a9 8b f3 ad 4c 33 d4 2e 40 c8 0c |...!.....L3..@..|
1177 1177 0230: 3b 90 53 39 db 48 02 34 83 48 d6 b3 99 13 d2 58 |;.S9.H.4.H.....X|
1178 1178 0240: 65 8e 71 ac a9 06 95 f2 c4 8e b4 08 6b d3 0c ae |e.q.........k...|
1179 1179 0250: d9 90 56 71 43 a7 a2 62 16 3e 50 63 d3 57 3c 2d |..VqC..b.>Pc.W<-|
1180 1180 0260: 9f 0f 34 05 08 d8 a6 4b 59 31 54 66 3a 45 0c 8a |..4....KY1Tf:E..|
1181 1181 0270: c7 90 3a f0 6a 83 1b f5 ca fb 80 2b 50 06 fb 51 |..:.j......+P..Q|
1182 1182 0280: 7e a6 a4 d4 81 44 82 21 54 00 5b 1a 30 83 62 a3 |~....D.!T.[.0.b.|
1183 1183 0290: 18 b6 24 19 1e 45 df 4d 5c db a6 af 5b ac 90 fa |..$..E.M\...[...|
1184 1184 02a0: 3e ed f9 ec 4c ba 36 ee d8 60 20 a7 c7 3b cb d1 |>...L.6..` ..;..|
1185 1185 02b0: 90 43 7d 27 16 50 5d ad f4 14 07 0b 90 5c cc 6b |.C}'.P]......\.k|
1186 1186 02c0: 8d 3f a6 88 f4 34 37 a8 cf 14 63 36 19 f7 3e 28 |.?...47...c6..>(|
1187 1187 02d0: de 99 e8 16 a4 9d 0d 40 a1 a7 24 52 14 a6 72 62 |.......@..$R..rb|
1188 1188 02e0: 59 5a ca 2d e5 51 90 78 88 d9 c6 c7 21 d0 f7 46 |YZ.-.Q.x....!..F|
1189 1189 02f0: b2 04 46 44 4e 20 9c 12 b1 03 4e 25 e0 a9 0c 58 |..FDN ....N%...X|
1190 1190 0300: 5b 1d 3c 93 20 01 51 de a9 1c 69 23 32 46 14 b4 |[.<. .Q...i#2F..|
1191 1191 0310: 90 db 17 98 98 50 03 90 29 aa 40 b0 13 d8 43 d2 |.....P..).@...C.|
1192 1192 0320: 5f c5 9d eb f3 f2 ad 41 e8 7a a9 ed a1 58 84 a6 |_......A.z...X..|
1193 1193 0330: 42 bf d6 fc 24 82 c1 20 32 26 4a 15 a6 1d 29 7f |B...$.. 2&J...).|
1194 1194 0340: 7e f4 3d 07 bc 62 9a 5b ec 44 3d 72 1d 41 8b 5c |~.=..b.[.D=r.A.\|
1195 1195 0350: 80 de 0e 62 9a 2e f8 83 00 d5 07 a0 9c c6 74 98 |...b..........t.|
1196 1196 0360: 11 b2 5e a9 38 02 03 ee fd 86 5c f4 86 b3 ae da |..^.8.....\.....|
1197 1197 0370: 05 94 01 c5 c6 ea 18 e6 ba 2a ba b3 04 5c 96 89 |.........*...\..|
1198 1198 0380: 72 63 5b 10 11 f6 67 34 98 cb e4 c0 4e fa e6 99 |rc[...g4....N...|
1199 1199 0390: 19 6e 50 e8 26 8d 0c 17 e0 be ef e1 8e 02 6f 32 |.nP.&.........o2|
1200 1200 03a0: 82 dc 26 f8 a1 08 f3 8a 0d f3 c4 75 00 48 73 b8 |..&........u.Hs.|
1201 1201 03b0: be 3b 0d 7f d0 fd c7 78 96 ec e0 03 80 68 4d 8d |.;.....x.....hM.|
1202 1202 03c0: 43 8c d7 68 58 f9 50 f0 18 cb 21 58 1b 60 cd 1f |C..hX.P...!X.`..|
1203 1203 03d0: 84 36 2e 16 1f 0a f7 4e 8f eb df 01 2d c2 79 0b |.6.....N....-.y.|
1204 1204 03e0: f7 24 ea 0d e8 59 86 51 6e 1c 30 a3 ad 2f ee 8c |.$...Y.Qn.0../..|
1205 1205 03f0: 90 c8 84 d5 e8 34 c1 95 b2 c9 f6 4d 87 1c 7d 19 |.....4.....M..}.|
1206 1206 0400: d6 41 58 56 7a e0 6c ba 10 c7 e8 33 39 36 96 e7 |.AXVz.l....396..|
1207 1207 0410: d2 f9 59 9a 08 95 48 38 e7 0b b7 0a 24 67 c4 39 |..Y...H8....$g.9|
1208 1208 0420: 8b 43 88 57 9c 01 f5 61 b5 e1 27 41 7e af 83 fe |.C.W...a..'A~...|
1209 1209 0430: 2e e4 8a 70 a1 21 46 96 30 7a |...p.!F.0z|
1210 1210 $ hg debugbundle ../rev.hg2.bz
1211 1211 Stream params: {Compression: BZ}
1212 1212 changegroup -- {} (mandatory: False)
1213 1213 32af7686d403cf45b5d95f2d70cebea587ac806a
1214 1214 9520eea781bcca16c1e15acc0ba14335a0e8e5ba
1215 1215 eea13746799a9e0bfd88f29d3c2e9dc9389f524f
1216 1216 02de42196ebee42ef284b6780a87cdc96e8eaab6
1217 1217 $ hg unbundle ../rev.hg2.bz
1218 1218 adding changesets
1219 1219 adding manifests
1220 1220 adding file changes
1221 1221 added 0 changesets with 0 changes to 3 files
1222 1222 (run 'hg update' to get a working copy)
1223 1223
1224 1224 unknown compression while unbundling
1225 1225 -----------------------------
1226 1226
1227 1227 $ hg bundle2 --param Compression=FooBarUnknown --rev '8+7+5+4' ../rev.hg2.bz
1228 1228 $ cat ../rev.hg2.bz | hg statbundle2
1229 1229 abort: unknown parameters: Stream Parameter - Compression='FooBarUnknown'
1230 1230 [255]
1231 1231 $ hg unbundle ../rev.hg2.bz
1232 1232 abort: ../rev.hg2.bz: unknown bundle feature, Stream Parameter - Compression='FooBarUnknown'
1233 1233 (see https://mercurial-scm.org/wiki/BundleFeature for more information)
1234 1234 [255]
1235 1235
1236 1236 $ cd ..
@@ -1,1892 +1,1892
1 1 ============================================================================================
2 2 Test cases where there are race condition between two clients pushing to the same repository
3 3 ============================================================================================
4 4
5 5 This file tests cases where two clients push to a server at the same time. The
6 6 "raced" client is done preparing it push bundle when the "racing" client
7 7 perform its push. The "raced" client starts its actual push after the "racing"
8 8 client push is fully complete.
9 9
10 10 A set of extension and shell functions ensures this scheduling.
11 11
12 12 $ cat >> delaypush.py << EOF
13 13 > """small extension orchestrate push race
14 14 >
15 15 > Client with the extensions will create a file when ready and get stuck until
16 16 > a file is created."""
17 17 >
18 18 > import errno
19 19 > import os
20 20 > import time
21 21 >
22 22 > from mercurial import (
23 23 > exchange,
24 24 > extensions,
25 25 > registrar,
26 26 > )
27 27 >
28 28 > configtable = {}
29 29 > configitem = registrar.configitem(configtable)
30 30 >
31 31 > configitem('delaypush', 'ready-path',
32 32 > default=None,
33 33 > )
34 34 > configitem('delaypush', 'release-path',
35 35 > default=None,
36 36 > )
37 37 >
38 38 > def delaypush(orig, pushop):
39 39 > # notify we are done preparing
40 40 > ui = pushop.repo.ui
41 41 > readypath = ui.config('delaypush', 'ready-path')
42 42 > if readypath is not None:
43 43 > with open(readypath, 'w') as r:
44 44 > r.write('foo')
45 45 > ui.status('wrote ready: %s\n' % readypath)
46 46 > # now wait for the other process to be done
47 47 > watchpath = ui.config('delaypush', 'release-path')
48 48 > if watchpath is not None:
49 49 > ui.status('waiting on: %s\n' % watchpath)
50 50 > limit = 100
51 51 > while 0 < limit and not os.path.exists(watchpath):
52 52 > limit -= 1
53 53 > time.sleep(0.1)
54 54 > if limit <= 0:
55 55 > ui.warn('exiting without watchfile: %s' % watchpath)
56 56 > else:
57 57 > # delete the file at the end of the push
58 58 > def delete():
59 59 > try:
60 60 > os.unlink(watchpath)
61 61 > except OSError as exc:
62 62 > if exc.errno != errno.ENOENT:
63 63 > raise
64 64 > ui.atexit(delete)
65 65 > return orig(pushop)
66 66 >
67 67 > def uisetup(ui):
68 68 > extensions.wrapfunction(exchange, '_pushbundle2', delaypush)
69 69 > EOF
70 70
71 71 $ waiton () {
72 72 > # wait for a file to be created (then delete it)
73 73 > count=100
74 74 > while [ ! -f $1 ] ;
75 75 > do
76 76 > sleep 0.1;
77 77 > count=`expr $count - 1`;
78 78 > if [ $count -lt 0 ];
79 79 > then
80 80 > break
81 81 > fi;
82 82 > done
83 83 > [ -f $1 ] || echo "ready file still missing: $1"
84 84 > rm -f $1
85 85 > }
86 86
87 87 $ release () {
88 88 > # create a file and wait for it be deleted
89 89 > count=100
90 90 > touch $1
91 91 > while [ -f $1 ] ;
92 92 > do
93 93 > sleep 0.1;
94 94 > count=`expr $count - 1`;
95 95 > if [ $count -lt 0 ];
96 96 > then
97 97 > break
98 98 > fi;
99 99 > done
100 100 > [ ! -f $1 ] || echo "delay file still exist: $1"
101 101 > }
102 102
103 103 $ cat >> $HGRCPATH << EOF
104 104 > [ui]
105 105 > ssh = "$PYTHON" "$TESTDIR/dummyssh"
106 106 > # simplify output
107 107 > logtemplate = {node|short} {desc} ({branch})
108 108 > [phases]
109 109 > publish = no
110 110 > [experimental]
111 111 > evolution=true
112 112 > [alias]
113 113 > graph = log -G --rev 'sort(all(), "topo")'
114 114 > EOF
115 115
116 116 We tests multiple cases:
117 117 * strict: no race detected,
118 118 * unrelated: race on unrelated heads are allowed.
119 119
120 120 #testcases strict unrelated
121 121
122 122 #if unrelated
123 123
124 124 $ cat >> $HGRCPATH << EOF
125 125 > [server]
126 126 > concurrent-push-mode = check-related
127 127 > EOF
128 128
129 129 #endif
130 130
131 131 Setup
132 132 -----
133 133
134 134 create a repo with one root
135 135
136 136 $ hg init server
137 137 $ cd server
138 138 $ echo root > root
139 139 $ hg ci -Am "C-ROOT"
140 140 adding root
141 141 $ cd ..
142 142
143 143 clone it in two clients
144 144
145 145 $ hg clone ssh://user@dummy/server client-racy
146 146 requesting all changes
147 147 adding changesets
148 148 adding manifests
149 149 adding file changes
150 150 added 1 changesets with 1 changes to 1 files
151 151 new changesets 842e2fac6304 (1 drafts)
152 152 updating to branch default
153 153 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
154 154 $ hg clone ssh://user@dummy/server client-other
155 155 requesting all changes
156 156 adding changesets
157 157 adding manifests
158 158 adding file changes
159 159 added 1 changesets with 1 changes to 1 files
160 160 new changesets 842e2fac6304 (1 drafts)
161 161 updating to branch default
162 162 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
163 163
164 164 setup one to allow race on push
165 165
166 166 $ cat >> client-racy/.hg/hgrc << EOF
167 167 > [extensions]
168 168 > delaypush = $TESTTMP/delaypush.py
169 169 > [delaypush]
170 170 > ready-path = $TESTTMP/readyfile
171 171 > release-path = $TESTTMP/watchfile
172 172 > EOF
173 173
174 174 Simple race, both try to push to the server at the same time
175 175 ------------------------------------------------------------
176 176
177 177 Both try to replace the same head
178 178
179 179 # a
180 180 # | b
181 181 # |/
182 182 # *
183 183
184 184 Creating changesets
185 185
186 186 $ echo b > client-other/a
187 187 $ hg -R client-other/ add client-other/a
188 188 $ hg -R client-other/ commit -m "C-A"
189 189 $ echo b > client-racy/b
190 190 $ hg -R client-racy/ add client-racy/b
191 191 $ hg -R client-racy/ commit -m "C-B"
192 192
193 193 Pushing
194 194
195 195 $ hg -R client-racy push -r 'tip' > ./push-log 2>&1 &
196 196
197 197 $ waiton $TESTTMP/readyfile
198 198
199 199 $ hg -R client-other push -r 'tip'
200 200 pushing to ssh://user@dummy/server
201 201 searching for changes
202 202 remote: adding changesets
203 203 remote: adding manifests
204 204 remote: adding file changes
205 205 remote: added 1 changesets with 1 changes to 1 files
206 206
207 207 $ release $TESTTMP/watchfile
208 208
209 209 Check the result of the push
210 210
211 211 $ cat ./push-log
212 212 pushing to ssh://user@dummy/server
213 213 searching for changes
214 214 wrote ready: $TESTTMP/readyfile
215 215 waiting on: $TESTTMP/watchfile
216 216 abort: push failed:
217 'repository changed while pushing - please try again'
217 'remote repository changed while pushing - please try again'
218 218
219 219 $ hg -R server graph
220 220 o 98217d5a1659 C-A (default)
221 221 |
222 222 @ 842e2fac6304 C-ROOT (default)
223 223
224 224
225 225 Pushing on two different heads
226 226 ------------------------------
227 227
228 228 Both try to replace a different head
229 229
230 230 # a b
231 231 # | |
232 232 # * *
233 233 # |/
234 234 # *
235 235
236 236 (resync-all)
237 237
238 238 $ hg -R ./server pull ./client-racy
239 239 pulling from ./client-racy
240 240 searching for changes
241 241 adding changesets
242 242 adding manifests
243 243 adding file changes
244 244 added 1 changesets with 1 changes to 1 files (+1 heads)
245 245 new changesets a9149a1428e2 (1 drafts)
246 246 (run 'hg heads' to see heads, 'hg merge' to merge)
247 247 $ hg -R ./client-other pull
248 248 pulling from ssh://user@dummy/server
249 249 searching for changes
250 250 adding changesets
251 251 adding manifests
252 252 adding file changes
253 253 added 1 changesets with 1 changes to 1 files (+1 heads)
254 254 new changesets a9149a1428e2 (1 drafts)
255 255 (run 'hg heads' to see heads, 'hg merge' to merge)
256 256 $ hg -R ./client-racy pull
257 257 pulling from ssh://user@dummy/server
258 258 searching for changes
259 259 adding changesets
260 260 adding manifests
261 261 adding file changes
262 262 added 1 changesets with 1 changes to 1 files (+1 heads)
263 263 new changesets 98217d5a1659 (1 drafts)
264 264 (run 'hg heads' to see heads, 'hg merge' to merge)
265 265
266 266 $ hg -R server graph
267 267 o a9149a1428e2 C-B (default)
268 268 |
269 269 | o 98217d5a1659 C-A (default)
270 270 |/
271 271 @ 842e2fac6304 C-ROOT (default)
272 272
273 273
274 274 Creating changesets
275 275
276 276 $ echo aa >> client-other/a
277 277 $ hg -R client-other/ commit -m "C-C"
278 278 $ echo bb >> client-racy/b
279 279 $ hg -R client-racy/ commit -m "C-D"
280 280
281 281 Pushing
282 282
283 283 $ hg -R client-racy push -r 'tip' > ./push-log 2>&1 &
284 284
285 285 $ waiton $TESTTMP/readyfile
286 286
287 287 $ hg -R client-other push -r 'tip'
288 288 pushing to ssh://user@dummy/server
289 289 searching for changes
290 290 remote: adding changesets
291 291 remote: adding manifests
292 292 remote: adding file changes
293 293 remote: added 1 changesets with 1 changes to 1 files
294 294
295 295 $ release $TESTTMP/watchfile
296 296
297 297 Check the result of the push
298 298
299 299 #if strict
300 300 $ cat ./push-log
301 301 pushing to ssh://user@dummy/server
302 302 searching for changes
303 303 wrote ready: $TESTTMP/readyfile
304 304 waiting on: $TESTTMP/watchfile
305 305 abort: push failed:
306 'repository changed while pushing - please try again'
306 'remote repository changed while pushing - please try again'
307 307
308 308 $ hg -R server graph
309 309 o 51c544a58128 C-C (default)
310 310 |
311 311 o 98217d5a1659 C-A (default)
312 312 |
313 313 | o a9149a1428e2 C-B (default)
314 314 |/
315 315 @ 842e2fac6304 C-ROOT (default)
316 316
317 317 #endif
318 318 #if unrelated
319 319
320 320 (The two heads are unrelated, push should be allowed)
321 321
322 322 $ cat ./push-log
323 323 pushing to ssh://user@dummy/server
324 324 searching for changes
325 325 wrote ready: $TESTTMP/readyfile
326 326 waiting on: $TESTTMP/watchfile
327 327 remote: adding changesets
328 328 remote: adding manifests
329 329 remote: adding file changes
330 330 remote: added 1 changesets with 1 changes to 1 files
331 331
332 332 $ hg -R server graph
333 333 o 59e76faf78bd C-D (default)
334 334 |
335 335 o a9149a1428e2 C-B (default)
336 336 |
337 337 | o 51c544a58128 C-C (default)
338 338 | |
339 339 | o 98217d5a1659 C-A (default)
340 340 |/
341 341 @ 842e2fac6304 C-ROOT (default)
342 342
343 343 #endif
344 344
345 345 Pushing while someone creates a new head
346 346 -----------------------------------------
347 347
348 348 Pushing a new changeset while someone creates a new branch.
349 349
350 350 # a (raced)
351 351 # |
352 352 # * b
353 353 # |/
354 354 # *
355 355
356 356 (resync-all)
357 357
358 358 #if strict
359 359
360 360 $ hg -R ./server pull ./client-racy
361 361 pulling from ./client-racy
362 362 searching for changes
363 363 adding changesets
364 364 adding manifests
365 365 adding file changes
366 366 added 1 changesets with 1 changes to 1 files
367 367 new changesets 59e76faf78bd (1 drafts)
368 368 (run 'hg update' to get a working copy)
369 369
370 370 #endif
371 371 #if unrelated
372 372
373 373 $ hg -R ./server pull ./client-racy
374 374 pulling from ./client-racy
375 375 searching for changes
376 376 no changes found
377 377
378 378 #endif
379 379
380 380 $ hg -R ./client-other pull
381 381 pulling from ssh://user@dummy/server
382 382 searching for changes
383 383 adding changesets
384 384 adding manifests
385 385 adding file changes
386 386 added 1 changesets with 1 changes to 1 files
387 387 new changesets 59e76faf78bd (1 drafts)
388 388 (run 'hg update' to get a working copy)
389 389 $ hg -R ./client-racy pull
390 390 pulling from ssh://user@dummy/server
391 391 searching for changes
392 392 adding changesets
393 393 adding manifests
394 394 adding file changes
395 395 added 1 changesets with 1 changes to 1 files
396 396 new changesets 51c544a58128 (1 drafts)
397 397 (run 'hg update' to get a working copy)
398 398
399 399 $ hg -R server graph
400 400 o 59e76faf78bd C-D (default)
401 401 |
402 402 o a9149a1428e2 C-B (default)
403 403 |
404 404 | o 51c544a58128 C-C (default)
405 405 | |
406 406 | o 98217d5a1659 C-A (default)
407 407 |/
408 408 @ 842e2fac6304 C-ROOT (default)
409 409
410 410
411 411 Creating changesets
412 412
413 413 (new head)
414 414
415 415 $ hg -R client-other/ up 'desc("C-A")'
416 416 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
417 417 $ echo aaa >> client-other/a
418 418 $ hg -R client-other/ commit -m "C-E"
419 419 created new head
420 420
421 421 (children of existing head)
422 422
423 423 $ hg -R client-racy/ up 'desc("C-C")'
424 424 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
425 425 $ echo bbb >> client-racy/a
426 426 $ hg -R client-racy/ commit -m "C-F"
427 427
428 428 Pushing
429 429
430 430 $ hg -R client-racy push -r 'tip' > ./push-log 2>&1 &
431 431
432 432 $ waiton $TESTTMP/readyfile
433 433
434 434 $ hg -R client-other push -fr 'tip'
435 435 pushing to ssh://user@dummy/server
436 436 searching for changes
437 437 remote: adding changesets
438 438 remote: adding manifests
439 439 remote: adding file changes
440 440 remote: added 1 changesets with 1 changes to 1 files (+1 heads)
441 441
442 442 $ release $TESTTMP/watchfile
443 443
444 444 Check the result of the push
445 445
446 446 #if strict
447 447
448 448 $ cat ./push-log
449 449 pushing to ssh://user@dummy/server
450 450 searching for changes
451 451 wrote ready: $TESTTMP/readyfile
452 452 waiting on: $TESTTMP/watchfile
453 453 abort: push failed:
454 'repository changed while pushing - please try again'
454 'remote repository changed while pushing - please try again'
455 455
456 456 $ hg -R server graph
457 457 o d603e2c0cdd7 C-E (default)
458 458 |
459 459 | o 51c544a58128 C-C (default)
460 460 |/
461 461 o 98217d5a1659 C-A (default)
462 462 |
463 463 | o 59e76faf78bd C-D (default)
464 464 | |
465 465 | o a9149a1428e2 C-B (default)
466 466 |/
467 467 @ 842e2fac6304 C-ROOT (default)
468 468
469 469
470 470 #endif
471 471
472 472 #if unrelated
473 473
474 474 (The racing new head do not affect existing heads, push should go through)
475 475
476 476 $ cat ./push-log
477 477 pushing to ssh://user@dummy/server
478 478 searching for changes
479 479 wrote ready: $TESTTMP/readyfile
480 480 waiting on: $TESTTMP/watchfile
481 481 remote: adding changesets
482 482 remote: adding manifests
483 483 remote: adding file changes
484 484 remote: added 1 changesets with 1 changes to 1 files
485 485
486 486 $ hg -R server graph
487 487 o d9e379a8c432 C-F (default)
488 488 |
489 489 o 51c544a58128 C-C (default)
490 490 |
491 491 | o d603e2c0cdd7 C-E (default)
492 492 |/
493 493 o 98217d5a1659 C-A (default)
494 494 |
495 495 | o 59e76faf78bd C-D (default)
496 496 | |
497 497 | o a9149a1428e2 C-B (default)
498 498 |/
499 499 @ 842e2fac6304 C-ROOT (default)
500 500
501 501 #endif
502 502
503 503 Pushing touching different named branch (same topo): new branch raced
504 504 ---------------------------------------------------------------------
505 505
506 506 Pushing two children on the same head, one is a different named branch
507 507
508 508 # a (raced, branch-a)
509 509 # |
510 510 # | b (default branch)
511 511 # |/
512 512 # *
513 513
514 514 (resync-all)
515 515
516 516 #if strict
517 517
518 518 $ hg -R ./server pull ./client-racy
519 519 pulling from ./client-racy
520 520 searching for changes
521 521 adding changesets
522 522 adding manifests
523 523 adding file changes
524 524 added 1 changesets with 1 changes to 1 files
525 525 new changesets d9e379a8c432 (1 drafts)
526 526 (run 'hg update' to get a working copy)
527 527
528 528 #endif
529 529 #if unrelated
530 530
531 531 $ hg -R ./server pull ./client-racy
532 532 pulling from ./client-racy
533 533 searching for changes
534 534 no changes found
535 535
536 536 #endif
537 537
538 538 $ hg -R ./client-other pull
539 539 pulling from ssh://user@dummy/server
540 540 searching for changes
541 541 adding changesets
542 542 adding manifests
543 543 adding file changes
544 544 added 1 changesets with 1 changes to 1 files
545 545 new changesets d9e379a8c432 (1 drafts)
546 546 (run 'hg update' to get a working copy)
547 547 $ hg -R ./client-racy pull
548 548 pulling from ssh://user@dummy/server
549 549 searching for changes
550 550 adding changesets
551 551 adding manifests
552 552 adding file changes
553 553 added 1 changesets with 1 changes to 1 files (+1 heads)
554 554 new changesets d603e2c0cdd7 (1 drafts)
555 555 (run 'hg heads .' to see heads, 'hg merge' to merge)
556 556
557 557 $ hg -R server graph
558 558 o d9e379a8c432 C-F (default)
559 559 |
560 560 o 51c544a58128 C-C (default)
561 561 |
562 562 | o d603e2c0cdd7 C-E (default)
563 563 |/
564 564 o 98217d5a1659 C-A (default)
565 565 |
566 566 | o 59e76faf78bd C-D (default)
567 567 | |
568 568 | o a9149a1428e2 C-B (default)
569 569 |/
570 570 @ 842e2fac6304 C-ROOT (default)
571 571
572 572
573 573 Creating changesets
574 574
575 575 (update existing head)
576 576
577 577 $ hg -R client-other/ up 'desc("C-F")'
578 578 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
579 579 $ echo aaa >> client-other/a
580 580 $ hg -R client-other/ commit -m "C-G"
581 581
582 582 (new named branch from that existing head)
583 583
584 584 $ hg -R client-racy/ up 'desc("C-F")'
585 585 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
586 586 $ echo bbb >> client-racy/a
587 587 $ hg -R client-racy/ branch my-first-test-branch
588 588 marked working directory as branch my-first-test-branch
589 589 (branches are permanent and global, did you want a bookmark?)
590 590 $ hg -R client-racy/ commit -m "C-H"
591 591
592 592 Pushing
593 593
594 594 $ hg -R client-racy push -r 'tip' --new-branch > ./push-log 2>&1 &
595 595
596 596 $ waiton $TESTTMP/readyfile
597 597
598 598 $ hg -R client-other push -fr 'tip'
599 599 pushing to ssh://user@dummy/server
600 600 searching for changes
601 601 remote: adding changesets
602 602 remote: adding manifests
603 603 remote: adding file changes
604 604 remote: added 1 changesets with 1 changes to 1 files
605 605
606 606 $ release $TESTTMP/watchfile
607 607
608 608 Check the result of the push
609 609
610 610 #if strict
611 611 $ cat ./push-log
612 612 pushing to ssh://user@dummy/server
613 613 searching for changes
614 614 wrote ready: $TESTTMP/readyfile
615 615 waiting on: $TESTTMP/watchfile
616 616 abort: push failed:
617 'repository changed while pushing - please try again'
617 'remote repository changed while pushing - please try again'
618 618
619 619 $ hg -R server graph
620 620 o 75d69cba5402 C-G (default)
621 621 |
622 622 o d9e379a8c432 C-F (default)
623 623 |
624 624 o 51c544a58128 C-C (default)
625 625 |
626 626 | o d603e2c0cdd7 C-E (default)
627 627 |/
628 628 o 98217d5a1659 C-A (default)
629 629 |
630 630 | o 59e76faf78bd C-D (default)
631 631 | |
632 632 | o a9149a1428e2 C-B (default)
633 633 |/
634 634 @ 842e2fac6304 C-ROOT (default)
635 635
636 636 #endif
637 637 #if unrelated
638 638
639 639 (unrelated named branches are unrelated)
640 640
641 641 $ cat ./push-log
642 642 pushing to ssh://user@dummy/server
643 643 searching for changes
644 644 wrote ready: $TESTTMP/readyfile
645 645 waiting on: $TESTTMP/watchfile
646 646 remote: adding changesets
647 647 remote: adding manifests
648 648 remote: adding file changes
649 649 remote: added 1 changesets with 1 changes to 1 files (+1 heads)
650 650
651 651 $ hg -R server graph
652 652 o 833be552cfe6 C-H (my-first-test-branch)
653 653 |
654 654 | o 75d69cba5402 C-G (default)
655 655 |/
656 656 o d9e379a8c432 C-F (default)
657 657 |
658 658 o 51c544a58128 C-C (default)
659 659 |
660 660 | o d603e2c0cdd7 C-E (default)
661 661 |/
662 662 o 98217d5a1659 C-A (default)
663 663 |
664 664 | o 59e76faf78bd C-D (default)
665 665 | |
666 666 | o a9149a1428e2 C-B (default)
667 667 |/
668 668 @ 842e2fac6304 C-ROOT (default)
669 669
670 670 #endif
671 671
672 672 The racing new head do not affect existing heads, push should go through
673 673
674 674 pushing touching different named branch (same topo): old branch raced
675 675 ---------------------------------------------------------------------
676 676
677 677 Pushing two children on the same head, one is a different named branch
678 678
679 679 # a (raced, default-branch)
680 680 # |
681 681 # | b (new branch)
682 682 # |/
683 683 # * (default-branch)
684 684
685 685 (resync-all)
686 686
687 687 #if strict
688 688
689 689 $ hg -R ./server pull ./client-racy
690 690 pulling from ./client-racy
691 691 searching for changes
692 692 adding changesets
693 693 adding manifests
694 694 adding file changes
695 695 added 1 changesets with 1 changes to 1 files (+1 heads)
696 696 new changesets 833be552cfe6 (1 drafts)
697 697 (run 'hg heads .' to see heads, 'hg merge' to merge)
698 698
699 699 #endif
700 700 #if unrelated
701 701
702 702 $ hg -R ./server pull ./client-racy
703 703 pulling from ./client-racy
704 704 searching for changes
705 705 no changes found
706 706
707 707 #endif
708 708
709 709 $ hg -R ./client-other pull
710 710 pulling from ssh://user@dummy/server
711 711 searching for changes
712 712 adding changesets
713 713 adding manifests
714 714 adding file changes
715 715 added 1 changesets with 1 changes to 1 files (+1 heads)
716 716 new changesets 833be552cfe6 (1 drafts)
717 717 (run 'hg heads .' to see heads, 'hg merge' to merge)
718 718 $ hg -R ./client-racy pull
719 719 pulling from ssh://user@dummy/server
720 720 searching for changes
721 721 adding changesets
722 722 adding manifests
723 723 adding file changes
724 724 added 1 changesets with 1 changes to 1 files (+1 heads)
725 725 new changesets 75d69cba5402 (1 drafts)
726 726 (run 'hg heads' to see heads)
727 727
728 728 $ hg -R server graph
729 729 o 833be552cfe6 C-H (my-first-test-branch)
730 730 |
731 731 | o 75d69cba5402 C-G (default)
732 732 |/
733 733 o d9e379a8c432 C-F (default)
734 734 |
735 735 o 51c544a58128 C-C (default)
736 736 |
737 737 | o d603e2c0cdd7 C-E (default)
738 738 |/
739 739 o 98217d5a1659 C-A (default)
740 740 |
741 741 | o 59e76faf78bd C-D (default)
742 742 | |
743 743 | o a9149a1428e2 C-B (default)
744 744 |/
745 745 @ 842e2fac6304 C-ROOT (default)
746 746
747 747
748 748 Creating changesets
749 749
750 750 (new named branch from one head)
751 751
752 752 $ hg -R client-other/ up 'desc("C-G")'
753 753 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
754 754 $ echo aaa >> client-other/a
755 755 $ hg -R client-other/ branch my-second-test-branch
756 756 marked working directory as branch my-second-test-branch
757 757 $ hg -R client-other/ commit -m "C-I"
758 758
759 759 (children "updating" that same head)
760 760
761 761 $ hg -R client-racy/ up 'desc("C-G")'
762 762 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
763 763 $ echo bbb >> client-racy/a
764 764 $ hg -R client-racy/ commit -m "C-J"
765 765
766 766 Pushing
767 767
768 768 $ hg -R client-racy push -r 'tip' > ./push-log 2>&1 &
769 769
770 770 $ waiton $TESTTMP/readyfile
771 771
772 772 $ hg -R client-other push -fr 'tip' --new-branch
773 773 pushing to ssh://user@dummy/server
774 774 searching for changes
775 775 remote: adding changesets
776 776 remote: adding manifests
777 777 remote: adding file changes
778 778 remote: added 1 changesets with 1 changes to 1 files
779 779
780 780 $ release $TESTTMP/watchfile
781 781
782 782 Check the result of the push
783 783
784 784 #if strict
785 785
786 786 $ cat ./push-log
787 787 pushing to ssh://user@dummy/server
788 788 searching for changes
789 789 wrote ready: $TESTTMP/readyfile
790 790 waiting on: $TESTTMP/watchfile
791 791 abort: push failed:
792 'repository changed while pushing - please try again'
792 'remote repository changed while pushing - please try again'
793 793
794 794 $ hg -R server graph
795 795 o b35ed749f288 C-I (my-second-test-branch)
796 796 |
797 797 o 75d69cba5402 C-G (default)
798 798 |
799 799 | o 833be552cfe6 C-H (my-first-test-branch)
800 800 |/
801 801 o d9e379a8c432 C-F (default)
802 802 |
803 803 o 51c544a58128 C-C (default)
804 804 |
805 805 | o d603e2c0cdd7 C-E (default)
806 806 |/
807 807 o 98217d5a1659 C-A (default)
808 808 |
809 809 | o 59e76faf78bd C-D (default)
810 810 | |
811 811 | o a9149a1428e2 C-B (default)
812 812 |/
813 813 @ 842e2fac6304 C-ROOT (default)
814 814
815 815
816 816 #endif
817 817
818 818 #if unrelated
819 819
820 820 (unrelated named branches are unrelated)
821 821
822 822 $ cat ./push-log
823 823 pushing to ssh://user@dummy/server
824 824 searching for changes
825 825 wrote ready: $TESTTMP/readyfile
826 826 waiting on: $TESTTMP/watchfile
827 827 remote: adding changesets
828 828 remote: adding manifests
829 829 remote: adding file changes
830 830 remote: added 1 changesets with 1 changes to 1 files (+1 heads)
831 831
832 832 $ hg -R server graph
833 833 o 89420bf00fae C-J (default)
834 834 |
835 835 | o b35ed749f288 C-I (my-second-test-branch)
836 836 |/
837 837 o 75d69cba5402 C-G (default)
838 838 |
839 839 | o 833be552cfe6 C-H (my-first-test-branch)
840 840 |/
841 841 o d9e379a8c432 C-F (default)
842 842 |
843 843 o 51c544a58128 C-C (default)
844 844 |
845 845 | o d603e2c0cdd7 C-E (default)
846 846 |/
847 847 o 98217d5a1659 C-A (default)
848 848 |
849 849 | o 59e76faf78bd C-D (default)
850 850 | |
851 851 | o a9149a1428e2 C-B (default)
852 852 |/
853 853 @ 842e2fac6304 C-ROOT (default)
854 854
855 855
856 856 #endif
857 857
858 858 pushing racing push touch multiple heads
859 859 ----------------------------------------
860 860
861 861 There are multiple heads, but the racing push touch all of them
862 862
863 863 # a (raced)
864 864 # | b
865 865 # |/|
866 866 # * *
867 867 # |/
868 868 # *
869 869
870 870 (resync-all)
871 871
872 872 #if strict
873 873
874 874 $ hg -R ./server pull ./client-racy
875 875 pulling from ./client-racy
876 876 searching for changes
877 877 adding changesets
878 878 adding manifests
879 879 adding file changes
880 880 added 1 changesets with 1 changes to 1 files (+1 heads)
881 881 new changesets 89420bf00fae (1 drafts)
882 882 (run 'hg heads .' to see heads, 'hg merge' to merge)
883 883
884 884 #endif
885 885
886 886 #if unrelated
887 887
888 888 $ hg -R ./server pull ./client-racy
889 889 pulling from ./client-racy
890 890 searching for changes
891 891 no changes found
892 892
893 893 #endif
894 894
895 895 $ hg -R ./client-other pull
896 896 pulling from ssh://user@dummy/server
897 897 searching for changes
898 898 adding changesets
899 899 adding manifests
900 900 adding file changes
901 901 added 1 changesets with 1 changes to 1 files (+1 heads)
902 902 new changesets 89420bf00fae (1 drafts)
903 903 (run 'hg heads' to see heads)
904 904 $ hg -R ./client-racy pull
905 905 pulling from ssh://user@dummy/server
906 906 searching for changes
907 907 adding changesets
908 908 adding manifests
909 909 adding file changes
910 910 added 1 changesets with 1 changes to 1 files (+1 heads)
911 911 new changesets b35ed749f288 (1 drafts)
912 912 (run 'hg heads .' to see heads, 'hg merge' to merge)
913 913
914 914 $ hg -R server graph
915 915 o 89420bf00fae C-J (default)
916 916 |
917 917 | o b35ed749f288 C-I (my-second-test-branch)
918 918 |/
919 919 o 75d69cba5402 C-G (default)
920 920 |
921 921 | o 833be552cfe6 C-H (my-first-test-branch)
922 922 |/
923 923 o d9e379a8c432 C-F (default)
924 924 |
925 925 o 51c544a58128 C-C (default)
926 926 |
927 927 | o d603e2c0cdd7 C-E (default)
928 928 |/
929 929 o 98217d5a1659 C-A (default)
930 930 |
931 931 | o 59e76faf78bd C-D (default)
932 932 | |
933 933 | o a9149a1428e2 C-B (default)
934 934 |/
935 935 @ 842e2fac6304 C-ROOT (default)
936 936
937 937
938 938 Creating changesets
939 939
940 940 (merges heads)
941 941
942 942 $ hg -R client-other/ up 'desc("C-E")'
943 943 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
944 944 $ hg -R client-other/ merge 'desc("C-D")'
945 945 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
946 946 (branch merge, don't forget to commit)
947 947 $ hg -R client-other/ commit -m "C-K"
948 948
949 949 (update one head)
950 950
951 951 $ hg -R client-racy/ up 'desc("C-D")'
952 952 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
953 953 $ echo bbb >> client-racy/b
954 954 $ hg -R client-racy/ commit -m "C-L"
955 955
956 956 Pushing
957 957
958 958 $ hg -R client-racy push -r 'tip' > ./push-log 2>&1 &
959 959
960 960 $ waiton $TESTTMP/readyfile
961 961
962 962 $ hg -R client-other push -fr 'tip' --new-branch
963 963 pushing to ssh://user@dummy/server
964 964 searching for changes
965 965 remote: adding changesets
966 966 remote: adding manifests
967 967 remote: adding file changes
968 968 remote: added 1 changesets with 0 changes to 0 files (-1 heads)
969 969
970 970 $ release $TESTTMP/watchfile
971 971
972 972 Check the result of the push
973 973
974 974 $ cat ./push-log
975 975 pushing to ssh://user@dummy/server
976 976 searching for changes
977 977 wrote ready: $TESTTMP/readyfile
978 978 waiting on: $TESTTMP/watchfile
979 979 abort: push failed:
980 'repository changed while pushing - please try again'
980 'remote repository changed while pushing - please try again'
981 981
982 982 $ hg -R server graph
983 983 o be705100c623 C-K (default)
984 984 |\
985 985 | o d603e2c0cdd7 C-E (default)
986 986 | |
987 987 o | 59e76faf78bd C-D (default)
988 988 | |
989 989 | | o 89420bf00fae C-J (default)
990 990 | | |
991 991 | | | o b35ed749f288 C-I (my-second-test-branch)
992 992 | | |/
993 993 | | o 75d69cba5402 C-G (default)
994 994 | | |
995 995 | | | o 833be552cfe6 C-H (my-first-test-branch)
996 996 | | |/
997 997 | | o d9e379a8c432 C-F (default)
998 998 | | |
999 999 | | o 51c544a58128 C-C (default)
1000 1000 | |/
1001 1001 o | a9149a1428e2 C-B (default)
1002 1002 | |
1003 1003 | o 98217d5a1659 C-A (default)
1004 1004 |/
1005 1005 @ 842e2fac6304 C-ROOT (default)
1006 1006
1007 1007
1008 1008 pushing raced push touch multiple heads
1009 1009 ---------------------------------------
1010 1010
1011 1011 There are multiple heads, the raced push touch all of them
1012 1012
1013 1013 # b
1014 1014 # | a (raced)
1015 1015 # |/|
1016 1016 # * *
1017 1017 # |/
1018 1018 # *
1019 1019
1020 1020 (resync-all)
1021 1021
1022 1022 $ hg -R ./server pull ./client-racy
1023 1023 pulling from ./client-racy
1024 1024 searching for changes
1025 1025 adding changesets
1026 1026 adding manifests
1027 1027 adding file changes
1028 1028 added 1 changesets with 1 changes to 1 files (+1 heads)
1029 1029 new changesets cac2cead0ff0 (1 drafts)
1030 1030 (run 'hg heads .' to see heads, 'hg merge' to merge)
1031 1031 $ hg -R ./client-other pull
1032 1032 pulling from ssh://user@dummy/server
1033 1033 searching for changes
1034 1034 adding changesets
1035 1035 adding manifests
1036 1036 adding file changes
1037 1037 added 1 changesets with 1 changes to 1 files (+1 heads)
1038 1038 new changesets cac2cead0ff0 (1 drafts)
1039 1039 (run 'hg heads .' to see heads, 'hg merge' to merge)
1040 1040 $ hg -R ./client-racy pull
1041 1041 pulling from ssh://user@dummy/server
1042 1042 searching for changes
1043 1043 adding changesets
1044 1044 adding manifests
1045 1045 adding file changes
1046 1046 added 1 changesets with 0 changes to 0 files
1047 1047 new changesets be705100c623 (1 drafts)
1048 1048 (run 'hg update' to get a working copy)
1049 1049
1050 1050 $ hg -R server graph
1051 1051 o cac2cead0ff0 C-L (default)
1052 1052 |
1053 1053 | o be705100c623 C-K (default)
1054 1054 |/|
1055 1055 | o d603e2c0cdd7 C-E (default)
1056 1056 | |
1057 1057 o | 59e76faf78bd C-D (default)
1058 1058 | |
1059 1059 | | o 89420bf00fae C-J (default)
1060 1060 | | |
1061 1061 | | | o b35ed749f288 C-I (my-second-test-branch)
1062 1062 | | |/
1063 1063 | | o 75d69cba5402 C-G (default)
1064 1064 | | |
1065 1065 | | | o 833be552cfe6 C-H (my-first-test-branch)
1066 1066 | | |/
1067 1067 | | o d9e379a8c432 C-F (default)
1068 1068 | | |
1069 1069 | | o 51c544a58128 C-C (default)
1070 1070 | |/
1071 1071 o | a9149a1428e2 C-B (default)
1072 1072 | |
1073 1073 | o 98217d5a1659 C-A (default)
1074 1074 |/
1075 1075 @ 842e2fac6304 C-ROOT (default)
1076 1076
1077 1077
1078 1078 Creating changesets
1079 1079
1080 1080 (update existing head)
1081 1081
1082 1082 $ echo aaa >> client-other/a
1083 1083 $ hg -R client-other/ commit -m "C-M"
1084 1084
1085 1085 (merge heads)
1086 1086
1087 1087 $ hg -R client-racy/ merge 'desc("C-K")'
1088 1088 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1089 1089 (branch merge, don't forget to commit)
1090 1090 $ hg -R client-racy/ commit -m "C-N"
1091 1091
1092 1092 Pushing
1093 1093
1094 1094 $ hg -R client-racy push -r 'tip' > ./push-log 2>&1 &
1095 1095
1096 1096 $ waiton $TESTTMP/readyfile
1097 1097
1098 1098 $ hg -R client-other push -fr 'tip' --new-branch
1099 1099 pushing to ssh://user@dummy/server
1100 1100 searching for changes
1101 1101 remote: adding changesets
1102 1102 remote: adding manifests
1103 1103 remote: adding file changes
1104 1104 remote: added 1 changesets with 1 changes to 1 files
1105 1105
1106 1106 $ release $TESTTMP/watchfile
1107 1107
1108 1108 Check the result of the push
1109 1109
1110 1110 $ cat ./push-log
1111 1111 pushing to ssh://user@dummy/server
1112 1112 searching for changes
1113 1113 wrote ready: $TESTTMP/readyfile
1114 1114 waiting on: $TESTTMP/watchfile
1115 1115 abort: push failed:
1116 'repository changed while pushing - please try again'
1116 'remote repository changed while pushing - please try again'
1117 1117
1118 1118 $ hg -R server graph
1119 1119 o 6fd3090135df C-M (default)
1120 1120 |
1121 1121 o be705100c623 C-K (default)
1122 1122 |\
1123 1123 | o d603e2c0cdd7 C-E (default)
1124 1124 | |
1125 1125 +---o cac2cead0ff0 C-L (default)
1126 1126 | |
1127 1127 o | 59e76faf78bd C-D (default)
1128 1128 | |
1129 1129 | | o 89420bf00fae C-J (default)
1130 1130 | | |
1131 1131 | | | o b35ed749f288 C-I (my-second-test-branch)
1132 1132 | | |/
1133 1133 | | o 75d69cba5402 C-G (default)
1134 1134 | | |
1135 1135 | | | o 833be552cfe6 C-H (my-first-test-branch)
1136 1136 | | |/
1137 1137 | | o d9e379a8c432 C-F (default)
1138 1138 | | |
1139 1139 | | o 51c544a58128 C-C (default)
1140 1140 | |/
1141 1141 o | a9149a1428e2 C-B (default)
1142 1142 | |
1143 1143 | o 98217d5a1659 C-A (default)
1144 1144 |/
1145 1145 @ 842e2fac6304 C-ROOT (default)
1146 1146
1147 1147
1148 1148 racing commit push a new head behind another named branch
1149 1149 ---------------------------------------------------------
1150 1150
1151 1151 non-continuous branch are valid case, we tests for them.
1152 1152
1153 1153 # b (branch default)
1154 1154 # |
1155 1155 # o (branch foo)
1156 1156 # |
1157 1157 # | a (raced, branch default)
1158 1158 # |/
1159 1159 # * (branch foo)
1160 1160 # |
1161 1161 # * (branch default)
1162 1162
1163 1163 (resync-all + other branch)
1164 1164
1165 1165 $ hg -R ./server pull ./client-racy
1166 1166 pulling from ./client-racy
1167 1167 searching for changes
1168 1168 adding changesets
1169 1169 adding manifests
1170 1170 adding file changes
1171 1171 added 1 changesets with 0 changes to 0 files
1172 1172 new changesets 866a66e18630 (1 drafts)
1173 1173 (run 'hg update' to get a working copy)
1174 1174
1175 1175 (creates named branch on head)
1176 1176
1177 1177 $ hg -R ./server/ up 'desc("C-N")'
1178 1178 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
1179 1179 $ hg -R ./server/ branch other
1180 1180 marked working directory as branch other
1181 1181 $ hg -R ./server/ ci -m "C-Z"
1182 1182 $ hg -R ./server/ up null
1183 1183 0 files updated, 0 files merged, 3 files removed, 0 files unresolved
1184 1184
1185 1185 (sync client)
1186 1186
1187 1187 $ hg -R ./client-other pull
1188 1188 pulling from ssh://user@dummy/server
1189 1189 searching for changes
1190 1190 adding changesets
1191 1191 adding manifests
1192 1192 adding file changes
1193 1193 added 2 changesets with 0 changes to 0 files
1194 1194 new changesets 866a66e18630:55a6f1c01b48 (2 drafts)
1195 1195 (run 'hg update' to get a working copy)
1196 1196 $ hg -R ./client-racy pull
1197 1197 pulling from ssh://user@dummy/server
1198 1198 searching for changes
1199 1199 adding changesets
1200 1200 adding manifests
1201 1201 adding file changes
1202 1202 added 2 changesets with 1 changes to 1 files (+1 heads)
1203 1203 new changesets 6fd3090135df:55a6f1c01b48 (2 drafts)
1204 1204 (run 'hg heads .' to see heads, 'hg merge' to merge)
1205 1205
1206 1206 $ hg -R server graph
1207 1207 o 55a6f1c01b48 C-Z (other)
1208 1208 |
1209 1209 o 866a66e18630 C-N (default)
1210 1210 |\
1211 1211 +---o 6fd3090135df C-M (default)
1212 1212 | |
1213 1213 | o cac2cead0ff0 C-L (default)
1214 1214 | |
1215 1215 o | be705100c623 C-K (default)
1216 1216 |\|
1217 1217 o | d603e2c0cdd7 C-E (default)
1218 1218 | |
1219 1219 | o 59e76faf78bd C-D (default)
1220 1220 | |
1221 1221 | | o 89420bf00fae C-J (default)
1222 1222 | | |
1223 1223 | | | o b35ed749f288 C-I (my-second-test-branch)
1224 1224 | | |/
1225 1225 | | o 75d69cba5402 C-G (default)
1226 1226 | | |
1227 1227 | | | o 833be552cfe6 C-H (my-first-test-branch)
1228 1228 | | |/
1229 1229 | | o d9e379a8c432 C-F (default)
1230 1230 | | |
1231 1231 +---o 51c544a58128 C-C (default)
1232 1232 | |
1233 1233 | o a9149a1428e2 C-B (default)
1234 1234 | |
1235 1235 o | 98217d5a1659 C-A (default)
1236 1236 |/
1237 1237 o 842e2fac6304 C-ROOT (default)
1238 1238
1239 1239
1240 1240 Creating changesets
1241 1241
1242 1242 (update default head through another named branch one)
1243 1243
1244 1244 $ hg -R client-other/ up 'desc("C-Z")'
1245 1245 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
1246 1246 $ echo aaa >> client-other/a
1247 1247 $ hg -R client-other/ commit -m "C-O"
1248 1248 $ echo aaa >> client-other/a
1249 1249 $ hg -R client-other/ branch --force default
1250 1250 marked working directory as branch default
1251 1251 $ hg -R client-other/ commit -m "C-P"
1252 1252 created new head
1253 1253
1254 1254 (update default head)
1255 1255
1256 1256 $ hg -R client-racy/ up 'desc("C-Z")'
1257 1257 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
1258 1258 $ echo bbb >> client-other/a
1259 1259 $ hg -R client-racy/ branch --force default
1260 1260 marked working directory as branch default
1261 1261 $ hg -R client-racy/ commit -m "C-Q"
1262 1262 created new head
1263 1263
1264 1264 Pushing
1265 1265
1266 1266 $ hg -R client-racy push -r 'tip' > ./push-log 2>&1 &
1267 1267
1268 1268 $ waiton $TESTTMP/readyfile
1269 1269
1270 1270 $ hg -R client-other push -fr 'tip' --new-branch
1271 1271 pushing to ssh://user@dummy/server
1272 1272 searching for changes
1273 1273 remote: adding changesets
1274 1274 remote: adding manifests
1275 1275 remote: adding file changes
1276 1276 remote: added 2 changesets with 1 changes to 1 files
1277 1277
1278 1278 $ release $TESTTMP/watchfile
1279 1279
1280 1280 Check the result of the push
1281 1281
1282 1282 $ cat ./push-log
1283 1283 pushing to ssh://user@dummy/server
1284 1284 searching for changes
1285 1285 wrote ready: $TESTTMP/readyfile
1286 1286 waiting on: $TESTTMP/watchfile
1287 1287 abort: push failed:
1288 'repository changed while pushing - please try again'
1288 'remote repository changed while pushing - please try again'
1289 1289
1290 1290 $ hg -R server graph
1291 1291 o 1b58ee3f79e5 C-P (default)
1292 1292 |
1293 1293 o d0a85b2252a9 C-O (other)
1294 1294 |
1295 1295 o 55a6f1c01b48 C-Z (other)
1296 1296 |
1297 1297 o 866a66e18630 C-N (default)
1298 1298 |\
1299 1299 +---o 6fd3090135df C-M (default)
1300 1300 | |
1301 1301 | o cac2cead0ff0 C-L (default)
1302 1302 | |
1303 1303 o | be705100c623 C-K (default)
1304 1304 |\|
1305 1305 o | d603e2c0cdd7 C-E (default)
1306 1306 | |
1307 1307 | o 59e76faf78bd C-D (default)
1308 1308 | |
1309 1309 | | o 89420bf00fae C-J (default)
1310 1310 | | |
1311 1311 | | | o b35ed749f288 C-I (my-second-test-branch)
1312 1312 | | |/
1313 1313 | | o 75d69cba5402 C-G (default)
1314 1314 | | |
1315 1315 | | | o 833be552cfe6 C-H (my-first-test-branch)
1316 1316 | | |/
1317 1317 | | o d9e379a8c432 C-F (default)
1318 1318 | | |
1319 1319 +---o 51c544a58128 C-C (default)
1320 1320 | |
1321 1321 | o a9149a1428e2 C-B (default)
1322 1322 | |
1323 1323 o | 98217d5a1659 C-A (default)
1324 1324 |/
1325 1325 o 842e2fac6304 C-ROOT (default)
1326 1326
1327 1327
1328 1328 raced commit push a new head behind another named branch
1329 1329 ---------------------------------------------------------
1330 1330
1331 1331 non-continuous branch are valid case, we tests for them.
1332 1332
1333 1333 # b (raced branch default)
1334 1334 # |
1335 1335 # o (branch foo)
1336 1336 # |
1337 1337 # | a (branch default)
1338 1338 # |/
1339 1339 # * (branch foo)
1340 1340 # |
1341 1341 # * (branch default)
1342 1342
1343 1343 (resync-all)
1344 1344
1345 1345 $ hg -R ./server pull ./client-racy
1346 1346 pulling from ./client-racy
1347 1347 searching for changes
1348 1348 adding changesets
1349 1349 adding manifests
1350 1350 adding file changes
1351 1351 added 1 changesets with 0 changes to 0 files (+1 heads)
1352 1352 new changesets b0ee3d6f51bc (1 drafts)
1353 1353 (run 'hg heads .' to see heads, 'hg merge' to merge)
1354 1354 $ hg -R ./client-other pull
1355 1355 pulling from ssh://user@dummy/server
1356 1356 searching for changes
1357 1357 adding changesets
1358 1358 adding manifests
1359 1359 adding file changes
1360 1360 added 1 changesets with 0 changes to 0 files (+1 heads)
1361 1361 new changesets b0ee3d6f51bc (1 drafts)
1362 1362 (run 'hg heads .' to see heads, 'hg merge' to merge)
1363 1363 $ hg -R ./client-racy pull
1364 1364 pulling from ssh://user@dummy/server
1365 1365 searching for changes
1366 1366 adding changesets
1367 1367 adding manifests
1368 1368 adding file changes
1369 1369 added 2 changesets with 1 changes to 1 files (+1 heads)
1370 1370 new changesets d0a85b2252a9:1b58ee3f79e5 (2 drafts)
1371 1371 (run 'hg heads .' to see heads, 'hg merge' to merge)
1372 1372
1373 1373 $ hg -R server graph
1374 1374 o b0ee3d6f51bc C-Q (default)
1375 1375 |
1376 1376 | o 1b58ee3f79e5 C-P (default)
1377 1377 | |
1378 1378 | o d0a85b2252a9 C-O (other)
1379 1379 |/
1380 1380 o 55a6f1c01b48 C-Z (other)
1381 1381 |
1382 1382 o 866a66e18630 C-N (default)
1383 1383 |\
1384 1384 +---o 6fd3090135df C-M (default)
1385 1385 | |
1386 1386 | o cac2cead0ff0 C-L (default)
1387 1387 | |
1388 1388 o | be705100c623 C-K (default)
1389 1389 |\|
1390 1390 o | d603e2c0cdd7 C-E (default)
1391 1391 | |
1392 1392 | o 59e76faf78bd C-D (default)
1393 1393 | |
1394 1394 | | o 89420bf00fae C-J (default)
1395 1395 | | |
1396 1396 | | | o b35ed749f288 C-I (my-second-test-branch)
1397 1397 | | |/
1398 1398 | | o 75d69cba5402 C-G (default)
1399 1399 | | |
1400 1400 | | | o 833be552cfe6 C-H (my-first-test-branch)
1401 1401 | | |/
1402 1402 | | o d9e379a8c432 C-F (default)
1403 1403 | | |
1404 1404 +---o 51c544a58128 C-C (default)
1405 1405 | |
1406 1406 | o a9149a1428e2 C-B (default)
1407 1407 | |
1408 1408 o | 98217d5a1659 C-A (default)
1409 1409 |/
1410 1410 o 842e2fac6304 C-ROOT (default)
1411 1411
1412 1412
1413 1413 Creating changesets
1414 1414
1415 1415 (update 'other' named branch head)
1416 1416
1417 1417 $ hg -R client-other/ up 'desc("C-P")'
1418 1418 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
1419 1419 $ echo aaa >> client-other/a
1420 1420 $ hg -R client-other/ branch --force other
1421 1421 marked working directory as branch other
1422 1422 $ hg -R client-other/ commit -m "C-R"
1423 1423 created new head
1424 1424
1425 1425 (update 'other named brnach through a 'default' changeset')
1426 1426
1427 1427 $ hg -R client-racy/ up 'desc("C-P")'
1428 1428 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1429 1429 $ echo bbb >> client-racy/a
1430 1430 $ hg -R client-racy/ commit -m "C-S"
1431 1431 $ echo bbb >> client-racy/a
1432 1432 $ hg -R client-racy/ branch --force other
1433 1433 marked working directory as branch other
1434 1434 $ hg -R client-racy/ commit -m "C-T"
1435 1435 created new head
1436 1436
1437 1437 Pushing
1438 1438
1439 1439 $ hg -R client-racy push -r 'tip' > ./push-log 2>&1 &
1440 1440
1441 1441 $ waiton $TESTTMP/readyfile
1442 1442
1443 1443 $ hg -R client-other push -fr 'tip' --new-branch
1444 1444 pushing to ssh://user@dummy/server
1445 1445 searching for changes
1446 1446 remote: adding changesets
1447 1447 remote: adding manifests
1448 1448 remote: adding file changes
1449 1449 remote: added 1 changesets with 1 changes to 1 files
1450 1450
1451 1451 $ release $TESTTMP/watchfile
1452 1452
1453 1453 Check the result of the push
1454 1454
1455 1455 $ cat ./push-log
1456 1456 pushing to ssh://user@dummy/server
1457 1457 searching for changes
1458 1458 wrote ready: $TESTTMP/readyfile
1459 1459 waiting on: $TESTTMP/watchfile
1460 1460 abort: push failed:
1461 'repository changed while pushing - please try again'
1461 'remote repository changed while pushing - please try again'
1462 1462
1463 1463 $ hg -R server graph
1464 1464 o de7b9e2ba3f6 C-R (other)
1465 1465 |
1466 1466 o 1b58ee3f79e5 C-P (default)
1467 1467 |
1468 1468 o d0a85b2252a9 C-O (other)
1469 1469 |
1470 1470 | o b0ee3d6f51bc C-Q (default)
1471 1471 |/
1472 1472 o 55a6f1c01b48 C-Z (other)
1473 1473 |
1474 1474 o 866a66e18630 C-N (default)
1475 1475 |\
1476 1476 +---o 6fd3090135df C-M (default)
1477 1477 | |
1478 1478 | o cac2cead0ff0 C-L (default)
1479 1479 | |
1480 1480 o | be705100c623 C-K (default)
1481 1481 |\|
1482 1482 o | d603e2c0cdd7 C-E (default)
1483 1483 | |
1484 1484 | o 59e76faf78bd C-D (default)
1485 1485 | |
1486 1486 | | o 89420bf00fae C-J (default)
1487 1487 | | |
1488 1488 | | | o b35ed749f288 C-I (my-second-test-branch)
1489 1489 | | |/
1490 1490 | | o 75d69cba5402 C-G (default)
1491 1491 | | |
1492 1492 | | | o 833be552cfe6 C-H (my-first-test-branch)
1493 1493 | | |/
1494 1494 | | o d9e379a8c432 C-F (default)
1495 1495 | | |
1496 1496 +---o 51c544a58128 C-C (default)
1497 1497 | |
1498 1498 | o a9149a1428e2 C-B (default)
1499 1499 | |
1500 1500 o | 98217d5a1659 C-A (default)
1501 1501 |/
1502 1502 o 842e2fac6304 C-ROOT (default)
1503 1503
1504 1504
1505 1505 raced commit push a new head obsoleting the one touched by the racing push
1506 1506 --------------------------------------------------------------------------
1507 1507
1508 1508 # b (racing)
1509 1509 # |
1510 1510 # ΓΈβ‡ β—” a (raced)
1511 1511 # |/
1512 1512 # *
1513 1513
1514 1514 (resync-all)
1515 1515
1516 1516 $ hg -R ./server pull ./client-racy
1517 1517 pulling from ./client-racy
1518 1518 searching for changes
1519 1519 adding changesets
1520 1520 adding manifests
1521 1521 adding file changes
1522 1522 added 2 changesets with 2 changes to 1 files (+1 heads)
1523 1523 new changesets 2efd43f7b5ba:3d57ed3c1091 (2 drafts)
1524 1524 (run 'hg heads .' to see heads, 'hg merge' to merge)
1525 1525 $ hg -R ./client-other pull
1526 1526 pulling from ssh://user@dummy/server
1527 1527 searching for changes
1528 1528 adding changesets
1529 1529 adding manifests
1530 1530 adding file changes
1531 1531 added 2 changesets with 2 changes to 1 files (+1 heads)
1532 1532 new changesets 2efd43f7b5ba:3d57ed3c1091 (2 drafts)
1533 1533 (run 'hg heads' to see heads, 'hg merge' to merge)
1534 1534 $ hg -R ./client-racy pull
1535 1535 pulling from ssh://user@dummy/server
1536 1536 searching for changes
1537 1537 adding changesets
1538 1538 adding manifests
1539 1539 adding file changes
1540 1540 added 1 changesets with 1 changes to 1 files (+1 heads)
1541 1541 new changesets de7b9e2ba3f6 (1 drafts)
1542 1542 (run 'hg heads' to see heads, 'hg merge' to merge)
1543 1543
1544 1544 $ hg -R server graph
1545 1545 o 3d57ed3c1091 C-T (other)
1546 1546 |
1547 1547 o 2efd43f7b5ba C-S (default)
1548 1548 |
1549 1549 | o de7b9e2ba3f6 C-R (other)
1550 1550 |/
1551 1551 o 1b58ee3f79e5 C-P (default)
1552 1552 |
1553 1553 o d0a85b2252a9 C-O (other)
1554 1554 |
1555 1555 | o b0ee3d6f51bc C-Q (default)
1556 1556 |/
1557 1557 o 55a6f1c01b48 C-Z (other)
1558 1558 |
1559 1559 o 866a66e18630 C-N (default)
1560 1560 |\
1561 1561 +---o 6fd3090135df C-M (default)
1562 1562 | |
1563 1563 | o cac2cead0ff0 C-L (default)
1564 1564 | |
1565 1565 o | be705100c623 C-K (default)
1566 1566 |\|
1567 1567 o | d603e2c0cdd7 C-E (default)
1568 1568 | |
1569 1569 | o 59e76faf78bd C-D (default)
1570 1570 | |
1571 1571 | | o 89420bf00fae C-J (default)
1572 1572 | | |
1573 1573 | | | o b35ed749f288 C-I (my-second-test-branch)
1574 1574 | | |/
1575 1575 | | o 75d69cba5402 C-G (default)
1576 1576 | | |
1577 1577 | | | o 833be552cfe6 C-H (my-first-test-branch)
1578 1578 | | |/
1579 1579 | | o d9e379a8c432 C-F (default)
1580 1580 | | |
1581 1581 +---o 51c544a58128 C-C (default)
1582 1582 | |
1583 1583 | o a9149a1428e2 C-B (default)
1584 1584 | |
1585 1585 o | 98217d5a1659 C-A (default)
1586 1586 |/
1587 1587 o 842e2fac6304 C-ROOT (default)
1588 1588
1589 1589
1590 1590 Creating changesets and markers
1591 1591
1592 1592 (continue existing head)
1593 1593
1594 1594 $ hg -R client-other/ up 'desc("C-Q")'
1595 1595 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1596 1596 $ echo aaa >> client-other/a
1597 1597 $ hg -R client-other/ commit -m "C-U"
1598 1598
1599 1599 (new topo branch obsoleting that same head)
1600 1600
1601 1601 $ hg -R client-racy/ up 'desc("C-Z")'
1602 1602 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1603 1603 $ echo bbb >> client-racy/a
1604 1604 $ hg -R client-racy/ branch --force default
1605 1605 marked working directory as branch default
1606 1606 $ hg -R client-racy/ commit -m "C-V"
1607 1607 created new head
1608 1608 $ ID_Q=`hg -R client-racy log -T '{node}\n' -r 'desc("C-Q")'`
1609 1609 $ ID_V=`hg -R client-racy log -T '{node}\n' -r 'desc("C-V")'`
1610 1610 $ hg -R client-racy debugobsolete $ID_Q $ID_V
1611 1611 obsoleted 1 changesets
1612 1612
1613 1613 Pushing
1614 1614
1615 1615 $ hg -R client-racy push -r 'tip' > ./push-log 2>&1 &
1616 1616
1617 1617 $ waiton $TESTTMP/readyfile
1618 1618
1619 1619 $ hg -R client-other push -fr 'tip' --new-branch
1620 1620 pushing to ssh://user@dummy/server
1621 1621 searching for changes
1622 1622 remote: adding changesets
1623 1623 remote: adding manifests
1624 1624 remote: adding file changes
1625 1625 remote: added 1 changesets with 0 changes to 0 files
1626 1626
1627 1627 $ release $TESTTMP/watchfile
1628 1628
1629 1629 Check the result of the push
1630 1630
1631 1631 $ cat ./push-log
1632 1632 pushing to ssh://user@dummy/server
1633 1633 searching for changes
1634 1634 wrote ready: $TESTTMP/readyfile
1635 1635 waiting on: $TESTTMP/watchfile
1636 1636 abort: push failed:
1637 'repository changed while pushing - please try again'
1637 'remote repository changed while pushing - please try again'
1638 1638
1639 1639 $ hg -R server debugobsolete
1640 1640 $ hg -R server graph
1641 1641 o a98a47d8b85b C-U (default)
1642 1642 |
1643 1643 o b0ee3d6f51bc C-Q (default)
1644 1644 |
1645 1645 | o 3d57ed3c1091 C-T (other)
1646 1646 | |
1647 1647 | o 2efd43f7b5ba C-S (default)
1648 1648 | |
1649 1649 | | o de7b9e2ba3f6 C-R (other)
1650 1650 | |/
1651 1651 | o 1b58ee3f79e5 C-P (default)
1652 1652 | |
1653 1653 | o d0a85b2252a9 C-O (other)
1654 1654 |/
1655 1655 o 55a6f1c01b48 C-Z (other)
1656 1656 |
1657 1657 o 866a66e18630 C-N (default)
1658 1658 |\
1659 1659 +---o 6fd3090135df C-M (default)
1660 1660 | |
1661 1661 | o cac2cead0ff0 C-L (default)
1662 1662 | |
1663 1663 o | be705100c623 C-K (default)
1664 1664 |\|
1665 1665 o | d603e2c0cdd7 C-E (default)
1666 1666 | |
1667 1667 | o 59e76faf78bd C-D (default)
1668 1668 | |
1669 1669 | | o 89420bf00fae C-J (default)
1670 1670 | | |
1671 1671 | | | o b35ed749f288 C-I (my-second-test-branch)
1672 1672 | | |/
1673 1673 | | o 75d69cba5402 C-G (default)
1674 1674 | | |
1675 1675 | | | o 833be552cfe6 C-H (my-first-test-branch)
1676 1676 | | |/
1677 1677 | | o d9e379a8c432 C-F (default)
1678 1678 | | |
1679 1679 +---o 51c544a58128 C-C (default)
1680 1680 | |
1681 1681 | o a9149a1428e2 C-B (default)
1682 1682 | |
1683 1683 o | 98217d5a1659 C-A (default)
1684 1684 |/
1685 1685 o 842e2fac6304 C-ROOT (default)
1686 1686
1687 1687
1688 1688 racing commit push a new head obsoleting the one touched by the raced push
1689 1689 --------------------------------------------------------------------------
1690 1690
1691 1691 (mirror test case of the previous one
1692 1692
1693 1693 # a (raced branch default)
1694 1694 # |
1695 1695 # ΓΈβ‡ β—” b (racing)
1696 1696 # |/
1697 1697 # *
1698 1698
1699 1699 (resync-all)
1700 1700
1701 1701 $ hg -R ./server pull ./client-racy
1702 1702 pulling from ./client-racy
1703 1703 searching for changes
1704 1704 adding changesets
1705 1705 adding manifests
1706 1706 adding file changes
1707 1707 added 1 changesets with 1 changes to 1 files (+1 heads)
1708 1708 1 new obsolescence markers
1709 1709 obsoleted 1 changesets
1710 1710 1 new orphan changesets
1711 1711 new changesets 720c5163ecf6 (1 drafts)
1712 1712 (run 'hg heads .' to see heads, 'hg merge' to merge)
1713 1713 $ hg -R ./client-other pull
1714 1714 pulling from ssh://user@dummy/server
1715 1715 searching for changes
1716 1716 adding changesets
1717 1717 adding manifests
1718 1718 adding file changes
1719 1719 added 1 changesets with 1 changes to 1 files (+1 heads)
1720 1720 1 new obsolescence markers
1721 1721 obsoleted 1 changesets
1722 1722 1 new orphan changesets
1723 1723 new changesets 720c5163ecf6 (1 drafts)
1724 1724 (run 'hg heads .' to see heads, 'hg merge' to merge)
1725 1725 $ hg -R ./client-racy pull
1726 1726 pulling from ssh://user@dummy/server
1727 1727 searching for changes
1728 1728 adding changesets
1729 1729 adding manifests
1730 1730 adding file changes
1731 1731 added 1 changesets with 0 changes to 0 files
1732 1732 1 new orphan changesets
1733 1733 new changesets a98a47d8b85b (1 drafts)
1734 1734 (run 'hg update' to get a working copy)
1735 1735
1736 1736 $ hg -R server debugobsolete
1737 1737 b0ee3d6f51bc4c0ca6d4f2907708027a6c376233 720c5163ecf64dcc6216bee2d62bf3edb1882499 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
1738 1738 $ hg -R server graph
1739 1739 o 720c5163ecf6 C-V (default)
1740 1740 |
1741 1741 | * a98a47d8b85b C-U (default)
1742 1742 | |
1743 1743 | x b0ee3d6f51bc C-Q (default)
1744 1744 |/
1745 1745 | o 3d57ed3c1091 C-T (other)
1746 1746 | |
1747 1747 | o 2efd43f7b5ba C-S (default)
1748 1748 | |
1749 1749 | | o de7b9e2ba3f6 C-R (other)
1750 1750 | |/
1751 1751 | o 1b58ee3f79e5 C-P (default)
1752 1752 | |
1753 1753 | o d0a85b2252a9 C-O (other)
1754 1754 |/
1755 1755 o 55a6f1c01b48 C-Z (other)
1756 1756 |
1757 1757 o 866a66e18630 C-N (default)
1758 1758 |\
1759 1759 +---o 6fd3090135df C-M (default)
1760 1760 | |
1761 1761 | o cac2cead0ff0 C-L (default)
1762 1762 | |
1763 1763 o | be705100c623 C-K (default)
1764 1764 |\|
1765 1765 o | d603e2c0cdd7 C-E (default)
1766 1766 | |
1767 1767 | o 59e76faf78bd C-D (default)
1768 1768 | |
1769 1769 | | o 89420bf00fae C-J (default)
1770 1770 | | |
1771 1771 | | | o b35ed749f288 C-I (my-second-test-branch)
1772 1772 | | |/
1773 1773 | | o 75d69cba5402 C-G (default)
1774 1774 | | |
1775 1775 | | | o 833be552cfe6 C-H (my-first-test-branch)
1776 1776 | | |/
1777 1777 | | o d9e379a8c432 C-F (default)
1778 1778 | | |
1779 1779 +---o 51c544a58128 C-C (default)
1780 1780 | |
1781 1781 | o a9149a1428e2 C-B (default)
1782 1782 | |
1783 1783 o | 98217d5a1659 C-A (default)
1784 1784 |/
1785 1785 o 842e2fac6304 C-ROOT (default)
1786 1786
1787 1787
1788 1788 Creating changesets and markers
1789 1789
1790 1790 (new topo branch obsoleting that same head)
1791 1791
1792 1792 $ hg -R client-other/ up 'desc("C-Q")'
1793 1793 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1794 1794 $ echo bbb >> client-other/a
1795 1795 $ hg -R client-other/ branch --force default
1796 1796 marked working directory as branch default
1797 1797 $ hg -R client-other/ commit -m "C-W"
1798 1798 1 new orphan changesets
1799 1799 created new head
1800 1800 $ ID_V=`hg -R client-other log -T '{node}\n' -r 'desc("C-V")'`
1801 1801 $ ID_W=`hg -R client-other log -T '{node}\n' -r 'desc("C-W")'`
1802 1802 $ hg -R client-other debugobsolete $ID_V $ID_W
1803 1803 obsoleted 1 changesets
1804 1804
1805 1805 (continue the same head)
1806 1806
1807 1807 $ echo aaa >> client-racy/a
1808 1808 $ hg -R client-racy/ commit -m "C-X"
1809 1809
1810 1810 Pushing
1811 1811
1812 1812 $ hg -R client-racy push -r 'tip' > ./push-log 2>&1 &
1813 1813
1814 1814 $ waiton $TESTTMP/readyfile
1815 1815
1816 1816 $ hg -R client-other push -fr 'tip' --new-branch
1817 1817 pushing to ssh://user@dummy/server
1818 1818 searching for changes
1819 1819 remote: adding changesets
1820 1820 remote: adding manifests
1821 1821 remote: adding file changes
1822 1822 remote: added 1 changesets with 0 changes to 1 files (+1 heads)
1823 1823 remote: 1 new obsolescence markers
1824 1824 remote: obsoleted 1 changesets
1825 1825 remote: 1 new orphan changesets
1826 1826
1827 1827 $ release $TESTTMP/watchfile
1828 1828
1829 1829 Check the result of the push
1830 1830
1831 1831 $ cat ./push-log
1832 1832 pushing to ssh://user@dummy/server
1833 1833 searching for changes
1834 1834 wrote ready: $TESTTMP/readyfile
1835 1835 waiting on: $TESTTMP/watchfile
1836 1836 abort: push failed:
1837 'repository changed while pushing - please try again'
1837 'remote repository changed while pushing - please try again'
1838 1838
1839 1839 $ hg -R server debugobsolete
1840 1840 b0ee3d6f51bc4c0ca6d4f2907708027a6c376233 720c5163ecf64dcc6216bee2d62bf3edb1882499 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
1841 1841 720c5163ecf64dcc6216bee2d62bf3edb1882499 39bc0598afe90ab18da460bafecc0fa953b77596 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
1842 1842 $ hg -R server graph --hidden
1843 1843 * 39bc0598afe9 C-W (default)
1844 1844 |
1845 1845 | * a98a47d8b85b C-U (default)
1846 1846 |/
1847 1847 x b0ee3d6f51bc C-Q (default)
1848 1848 |
1849 1849 | o 3d57ed3c1091 C-T (other)
1850 1850 | |
1851 1851 | o 2efd43f7b5ba C-S (default)
1852 1852 | |
1853 1853 | | o de7b9e2ba3f6 C-R (other)
1854 1854 | |/
1855 1855 | o 1b58ee3f79e5 C-P (default)
1856 1856 | |
1857 1857 | o d0a85b2252a9 C-O (other)
1858 1858 |/
1859 1859 | x 720c5163ecf6 C-V (default)
1860 1860 |/
1861 1861 o 55a6f1c01b48 C-Z (other)
1862 1862 |
1863 1863 o 866a66e18630 C-N (default)
1864 1864 |\
1865 1865 +---o 6fd3090135df C-M (default)
1866 1866 | |
1867 1867 | o cac2cead0ff0 C-L (default)
1868 1868 | |
1869 1869 o | be705100c623 C-K (default)
1870 1870 |\|
1871 1871 o | d603e2c0cdd7 C-E (default)
1872 1872 | |
1873 1873 | o 59e76faf78bd C-D (default)
1874 1874 | |
1875 1875 | | o 89420bf00fae C-J (default)
1876 1876 | | |
1877 1877 | | | o b35ed749f288 C-I (my-second-test-branch)
1878 1878 | | |/
1879 1879 | | o 75d69cba5402 C-G (default)
1880 1880 | | |
1881 1881 | | | o 833be552cfe6 C-H (my-first-test-branch)
1882 1882 | | |/
1883 1883 | | o d9e379a8c432 C-F (default)
1884 1884 | | |
1885 1885 +---o 51c544a58128 C-C (default)
1886 1886 | |
1887 1887 | o a9149a1428e2 C-B (default)
1888 1888 | |
1889 1889 o | 98217d5a1659 C-A (default)
1890 1890 |/
1891 1891 o 842e2fac6304 C-ROOT (default)
1892 1892
General Comments 0
You need to be logged in to leave comments. Login now