Mercurial > hg
comparison mercurial/bundle2.py @ 43077:687b865b95ad
formatting: byteify all mercurial/ and hgext/ string literals
Done with
python3.7 contrib/byteify-strings.py -i $(hg files 'set:mercurial/**.py - mercurial/thirdparty/** + hgext/**.py - hgext/fsmonitor/pywatchman/** - mercurial/__init__.py')
black -l 80 -t py33 -S $(hg files 'set:**.py - mercurial/thirdparty/** - "contrib/python-zstandard/**" - hgext/fsmonitor/pywatchman/**')
# skip-blame mass-reformatting only
Differential Revision: https://phab.mercurial-scm.org/D6972
author | Augie Fackler <augie@google.com> |
---|---|
date | Sun, 06 Oct 2019 09:48:39 -0400 |
parents | 2372284d9457 |
children | 86e4daa2d54c |
comparison
equal
deleted
inserted
replaced
43076:2372284d9457 | 43077:687b865b95ad |
---|---|
177 urlreq = util.urlreq | 177 urlreq = util.urlreq |
178 | 178 |
179 _pack = struct.pack | 179 _pack = struct.pack |
180 _unpack = struct.unpack | 180 _unpack = struct.unpack |
181 | 181 |
182 _fstreamparamsize = '>i' | 182 _fstreamparamsize = b'>i' |
183 _fpartheadersize = '>i' | 183 _fpartheadersize = b'>i' |
184 _fparttypesize = '>B' | 184 _fparttypesize = b'>B' |
185 _fpartid = '>I' | 185 _fpartid = b'>I' |
186 _fpayloadsize = '>i' | 186 _fpayloadsize = b'>i' |
187 _fpartparamcount = '>BB' | 187 _fpartparamcount = b'>BB' |
188 | 188 |
189 preferedchunksize = 32768 | 189 preferedchunksize = 32768 |
190 | 190 |
191 _parttypeforbidden = re.compile('[^a-zA-Z0-9_:-]') | 191 _parttypeforbidden = re.compile(b'[^a-zA-Z0-9_:-]') |
192 | 192 |
193 | 193 |
194 def outdebug(ui, message): | 194 def outdebug(ui, message): |
195 """debug regarding output stream (bundling)""" | 195 """debug regarding output stream (bundling)""" |
196 if ui.configbool('devel', 'bundle2.debug'): | 196 if ui.configbool(b'devel', b'bundle2.debug'): |
197 ui.debug('bundle2-output: %s\n' % message) | 197 ui.debug(b'bundle2-output: %s\n' % message) |
198 | 198 |
199 | 199 |
200 def indebug(ui, message): | 200 def indebug(ui, message): |
201 """debug on input stream (unbundling)""" | 201 """debug on input stream (unbundling)""" |
202 if ui.configbool('devel', 'bundle2.debug'): | 202 if ui.configbool(b'devel', b'bundle2.debug'): |
203 ui.debug('bundle2-input: %s\n' % message) | 203 ui.debug(b'bundle2-input: %s\n' % message) |
204 | 204 |
205 | 205 |
206 def validateparttype(parttype): | 206 def validateparttype(parttype): |
207 """raise ValueError if a parttype contains invalid character""" | 207 """raise ValueError if a parttype contains invalid character""" |
208 if _parttypeforbidden.search(parttype): | 208 if _parttypeforbidden.search(parttype): |
213 """return a struct format to read part parameter sizes | 213 """return a struct format to read part parameter sizes |
214 | 214 |
215 The number parameters is variable so we need to build that format | 215 The number parameters is variable so we need to build that format |
216 dynamically. | 216 dynamically. |
217 """ | 217 """ |
218 return '>' + ('BB' * nbparams) | 218 return b'>' + (b'BB' * nbparams) |
219 | 219 |
220 | 220 |
221 parthandlermapping = {} | 221 parthandlermapping = {} |
222 | 222 |
223 | 223 |
305 * a way to retrieve a transaction to add changes to the repo, | 305 * a way to retrieve a transaction to add changes to the repo, |
306 * a way to record the result of processing each part, | 306 * a way to record the result of processing each part, |
307 * a way to construct a bundle response when applicable. | 307 * a way to construct a bundle response when applicable. |
308 """ | 308 """ |
309 | 309 |
310 def __init__(self, repo, transactiongetter, captureoutput=True, source=''): | 310 def __init__(self, repo, transactiongetter, captureoutput=True, source=b''): |
311 self.repo = repo | 311 self.repo = repo |
312 self.ui = repo.ui | 312 self.ui = repo.ui |
313 self.records = unbundlerecords() | 313 self.records = unbundlerecords() |
314 self.reply = None | 314 self.reply = None |
315 self.captureoutput = captureoutput | 315 self.captureoutput = captureoutput |
335 return transaction | 335 return transaction |
336 | 336 |
337 def addhookargs(self, hookargs): | 337 def addhookargs(self, hookargs): |
338 if self.hookargs is None: | 338 if self.hookargs is None: |
339 raise error.ProgrammingError( | 339 raise error.ProgrammingError( |
340 'attempted to add hookargs to ' | 340 b'attempted to add hookargs to ' |
341 'operation after transaction started' | 341 b'operation after transaction started' |
342 ) | 342 ) |
343 self.hookargs.update(hookargs) | 343 self.hookargs.update(hookargs) |
344 | 344 |
345 | 345 |
346 class TransactionUnavailable(RuntimeError): | 346 class TransactionUnavailable(RuntimeError): |
356 | 356 |
357 | 357 |
358 def applybundle(repo, unbundler, tr, source, url=None, **kwargs): | 358 def applybundle(repo, unbundler, tr, source, url=None, **kwargs): |
359 # transform me into unbundler.apply() as soon as the freeze is lifted | 359 # transform me into unbundler.apply() as soon as the freeze is lifted |
360 if isinstance(unbundler, unbundle20): | 360 if isinstance(unbundler, unbundle20): |
361 tr.hookargs['bundle2'] = '1' | 361 tr.hookargs[b'bundle2'] = b'1' |
362 if source is not None and 'source' not in tr.hookargs: | 362 if source is not None and b'source' not in tr.hookargs: |
363 tr.hookargs['source'] = source | 363 tr.hookargs[b'source'] = source |
364 if url is not None and 'url' not in tr.hookargs: | 364 if url is not None and b'url' not in tr.hookargs: |
365 tr.hookargs['url'] = url | 365 tr.hookargs[b'url'] = url |
366 return processbundle(repo, unbundler, lambda: tr, source=source) | 366 return processbundle(repo, unbundler, lambda: tr, source=source) |
367 else: | 367 else: |
368 # the transactiongetter won't be used, but we might as well set it | 368 # the transactiongetter won't be used, but we might as well set it |
369 op = bundleoperation(repo, lambda: tr, source=source) | 369 op = bundleoperation(repo, lambda: tr, source=source) |
370 _processchangegroup(op, unbundler, tr, source, url, **kwargs) | 370 _processchangegroup(op, unbundler, tr, source, url, **kwargs) |
436 # that form if we need to. | 436 # that form if we need to. |
437 if seekerror: | 437 if seekerror: |
438 raise exc | 438 raise exc |
439 | 439 |
440 self.repo.ui.debug( | 440 self.repo.ui.debug( |
441 'bundle2-input-bundle: %i parts total\n' % self.count | 441 b'bundle2-input-bundle: %i parts total\n' % self.count |
442 ) | 442 ) |
443 | 443 |
444 | 444 |
445 def processbundle(repo, unbundler, transactiongetter=None, op=None, source=''): | 445 def processbundle(repo, unbundler, transactiongetter=None, op=None, source=b''): |
446 """This function process a bundle, apply effect to/from a repo | 446 """This function process a bundle, apply effect to/from a repo |
447 | 447 |
448 It iterates over each part then searches for and uses the proper handling | 448 It iterates over each part then searches for and uses the proper handling |
449 code to process the part. Parts are processed in order. | 449 code to process the part. Parts are processed in order. |
450 | 450 |
462 # todo: | 462 # todo: |
463 # - replace this is a init function soon. | 463 # - replace this is a init function soon. |
464 # - exception catching | 464 # - exception catching |
465 unbundler.params | 465 unbundler.params |
466 if repo.ui.debugflag: | 466 if repo.ui.debugflag: |
467 msg = ['bundle2-input-bundle:'] | 467 msg = [b'bundle2-input-bundle:'] |
468 if unbundler.params: | 468 if unbundler.params: |
469 msg.append(' %i params' % len(unbundler.params)) | 469 msg.append(b' %i params' % len(unbundler.params)) |
470 if op._gettransaction is None or op._gettransaction is _notransaction: | 470 if op._gettransaction is None or op._gettransaction is _notransaction: |
471 msg.append(' no-transaction') | 471 msg.append(b' no-transaction') |
472 else: | 472 else: |
473 msg.append(' with-transaction') | 473 msg.append(b' with-transaction') |
474 msg.append('\n') | 474 msg.append(b'\n') |
475 repo.ui.debug(''.join(msg)) | 475 repo.ui.debug(b''.join(msg)) |
476 | 476 |
477 processparts(repo, op, unbundler) | 477 processparts(repo, op, unbundler) |
478 | 478 |
479 return op | 479 return op |
480 | 480 |
485 _processpart(op, part) | 485 _processpart(op, part) |
486 | 486 |
487 | 487 |
488 def _processchangegroup(op, cg, tr, source, url, **kwargs): | 488 def _processchangegroup(op, cg, tr, source, url, **kwargs): |
489 ret = cg.apply(op.repo, tr, source, url, **kwargs) | 489 ret = cg.apply(op.repo, tr, source, url, **kwargs) |
490 op.records.add('changegroup', {'return': ret,}) | 490 op.records.add(b'changegroup', {b'return': ret,}) |
491 return ret | 491 return ret |
492 | 492 |
493 | 493 |
494 def _gethandler(op, part): | 494 def _gethandler(op, part): |
495 status = 'unknown' # used by debug output | 495 status = b'unknown' # used by debug output |
496 try: | 496 try: |
497 handler = parthandlermapping.get(part.type) | 497 handler = parthandlermapping.get(part.type) |
498 if handler is None: | 498 if handler is None: |
499 status = 'unsupported-type' | 499 status = b'unsupported-type' |
500 raise error.BundleUnknownFeatureError(parttype=part.type) | 500 raise error.BundleUnknownFeatureError(parttype=part.type) |
501 indebug(op.ui, 'found a handler for part %s' % part.type) | 501 indebug(op.ui, b'found a handler for part %s' % part.type) |
502 unknownparams = part.mandatorykeys - handler.params | 502 unknownparams = part.mandatorykeys - handler.params |
503 if unknownparams: | 503 if unknownparams: |
504 unknownparams = list(unknownparams) | 504 unknownparams = list(unknownparams) |
505 unknownparams.sort() | 505 unknownparams.sort() |
506 status = 'unsupported-params (%s)' % ', '.join(unknownparams) | 506 status = b'unsupported-params (%s)' % b', '.join(unknownparams) |
507 raise error.BundleUnknownFeatureError( | 507 raise error.BundleUnknownFeatureError( |
508 parttype=part.type, params=unknownparams | 508 parttype=part.type, params=unknownparams |
509 ) | 509 ) |
510 status = 'supported' | 510 status = b'supported' |
511 except error.BundleUnknownFeatureError as exc: | 511 except error.BundleUnknownFeatureError as exc: |
512 if part.mandatory: # mandatory parts | 512 if part.mandatory: # mandatory parts |
513 raise | 513 raise |
514 indebug(op.ui, 'ignoring unsupported advisory part %s' % exc) | 514 indebug(op.ui, b'ignoring unsupported advisory part %s' % exc) |
515 return # skip to part processing | 515 return # skip to part processing |
516 finally: | 516 finally: |
517 if op.ui.debugflag: | 517 if op.ui.debugflag: |
518 msg = ['bundle2-input-part: "%s"' % part.type] | 518 msg = [b'bundle2-input-part: "%s"' % part.type] |
519 if not part.mandatory: | 519 if not part.mandatory: |
520 msg.append(' (advisory)') | 520 msg.append(b' (advisory)') |
521 nbmp = len(part.mandatorykeys) | 521 nbmp = len(part.mandatorykeys) |
522 nbap = len(part.params) - nbmp | 522 nbap = len(part.params) - nbmp |
523 if nbmp or nbap: | 523 if nbmp or nbap: |
524 msg.append(' (params:') | 524 msg.append(b' (params:') |
525 if nbmp: | 525 if nbmp: |
526 msg.append(' %i mandatory' % nbmp) | 526 msg.append(b' %i mandatory' % nbmp) |
527 if nbap: | 527 if nbap: |
528 msg.append(' %i advisory' % nbmp) | 528 msg.append(b' %i advisory' % nbmp) |
529 msg.append(')') | 529 msg.append(b')') |
530 msg.append(' %s\n' % status) | 530 msg.append(b' %s\n' % status) |
531 op.ui.debug(''.join(msg)) | 531 op.ui.debug(b''.join(msg)) |
532 | 532 |
533 return handler | 533 return handler |
534 | 534 |
535 | 535 |
536 def _processpart(op, part): | 536 def _processpart(op, part): |
547 # parthandlermapping lookup (any KeyError raised by handler() | 547 # parthandlermapping lookup (any KeyError raised by handler() |
548 # itself represents a defect of a different variety). | 548 # itself represents a defect of a different variety). |
549 output = None | 549 output = None |
550 if op.captureoutput and op.reply is not None: | 550 if op.captureoutput and op.reply is not None: |
551 op.ui.pushbuffer(error=True, subproc=True) | 551 op.ui.pushbuffer(error=True, subproc=True) |
552 output = '' | 552 output = b'' |
553 try: | 553 try: |
554 handler(op, part) | 554 handler(op, part) |
555 finally: | 555 finally: |
556 if output is not None: | 556 if output is not None: |
557 output = op.ui.popbuffer() | 557 output = op.ui.popbuffer() |
558 if output: | 558 if output: |
559 outpart = op.reply.newpart('output', data=output, mandatory=False) | 559 outpart = op.reply.newpart(b'output', data=output, mandatory=False) |
560 outpart.addparam( | 560 outpart.addparam( |
561 'in-reply-to', pycompat.bytestr(part.id), mandatory=False | 561 b'in-reply-to', pycompat.bytestr(part.id), mandatory=False |
562 ) | 562 ) |
563 | 563 |
564 | 564 |
565 def decodecaps(blob): | 565 def decodecaps(blob): |
566 """decode a bundle2 caps bytes blob into a dictionary | 566 """decode a bundle2 caps bytes blob into a dictionary |
573 The values are always a list.""" | 573 The values are always a list.""" |
574 caps = {} | 574 caps = {} |
575 for line in blob.splitlines(): | 575 for line in blob.splitlines(): |
576 if not line: | 576 if not line: |
577 continue | 577 continue |
578 if '=' not in line: | 578 if b'=' not in line: |
579 key, vals = line, () | 579 key, vals = line, () |
580 else: | 580 else: |
581 key, vals = line.split('=', 1) | 581 key, vals = line.split(b'=', 1) |
582 vals = vals.split(',') | 582 vals = vals.split(b',') |
583 key = urlreq.unquote(key) | 583 key = urlreq.unquote(key) |
584 vals = [urlreq.unquote(v) for v in vals] | 584 vals = [urlreq.unquote(v) for v in vals] |
585 caps[key] = vals | 585 caps[key] = vals |
586 return caps | 586 return caps |
587 | 587 |
592 for ca in sorted(caps): | 592 for ca in sorted(caps): |
593 vals = caps[ca] | 593 vals = caps[ca] |
594 ca = urlreq.quote(ca) | 594 ca = urlreq.quote(ca) |
595 vals = [urlreq.quote(v) for v in vals] | 595 vals = [urlreq.quote(v) for v in vals] |
596 if vals: | 596 if vals: |
597 ca = "%s=%s" % (ca, ','.join(vals)) | 597 ca = b"%s=%s" % (ca, b','.join(vals)) |
598 chunks.append(ca) | 598 chunks.append(ca) |
599 return '\n'.join(chunks) | 599 return b'\n'.join(chunks) |
600 | 600 |
601 | 601 |
602 bundletypes = { | 602 bundletypes = { |
603 "": ("", 'UN'), # only when using unbundle on ssh and old http servers | 603 b"": (b"", b'UN'), # only when using unbundle on ssh and old http servers |
604 # since the unification ssh accepts a header but there | 604 # since the unification ssh accepts a header but there |
605 # is no capability signaling it. | 605 # is no capability signaling it. |
606 "HG20": (), # special-cased below | 606 b"HG20": (), # special-cased below |
607 "HG10UN": ("HG10UN", 'UN'), | 607 b"HG10UN": (b"HG10UN", b'UN'), |
608 "HG10BZ": ("HG10", 'BZ'), | 608 b"HG10BZ": (b"HG10", b'BZ'), |
609 "HG10GZ": ("HG10GZ", 'GZ'), | 609 b"HG10GZ": (b"HG10GZ", b'GZ'), |
610 } | 610 } |
611 | 611 |
612 # hgweb uses this list to communicate its preferred type | 612 # hgweb uses this list to communicate its preferred type |
613 bundlepriority = ['HG10GZ', 'HG10BZ', 'HG10UN'] | 613 bundlepriority = [b'HG10GZ', b'HG10BZ', b'HG10UN'] |
614 | 614 |
615 | 615 |
616 class bundle20(object): | 616 class bundle20(object): |
617 """represent an outgoing bundle2 container | 617 """represent an outgoing bundle2 container |
618 | 618 |
619 Use the `addparam` method to add stream level parameter. and `newpart` to | 619 Use the `addparam` method to add stream level parameter. and `newpart` to |
620 populate it. Then call `getchunks` to retrieve all the binary chunks of | 620 populate it. Then call `getchunks` to retrieve all the binary chunks of |
621 data that compose the bundle2 container.""" | 621 data that compose the bundle2 container.""" |
622 | 622 |
623 _magicstring = 'HG20' | 623 _magicstring = b'HG20' |
624 | 624 |
625 def __init__(self, ui, capabilities=()): | 625 def __init__(self, ui, capabilities=()): |
626 self.ui = ui | 626 self.ui = ui |
627 self._params = [] | 627 self._params = [] |
628 self._parts = [] | 628 self._parts = [] |
629 self.capabilities = dict(capabilities) | 629 self.capabilities = dict(capabilities) |
630 self._compengine = util.compengines.forbundletype('UN') | 630 self._compengine = util.compengines.forbundletype(b'UN') |
631 self._compopts = None | 631 self._compopts = None |
632 # If compression is being handled by a consumer of the raw | 632 # If compression is being handled by a consumer of the raw |
633 # data (e.g. the wire protocol), unsetting this flag tells | 633 # data (e.g. the wire protocol), unsetting this flag tells |
634 # consumers that the bundle is best left uncompressed. | 634 # consumers that the bundle is best left uncompressed. |
635 self.prefercompressed = True | 635 self.prefercompressed = True |
636 | 636 |
637 def setcompression(self, alg, compopts=None): | 637 def setcompression(self, alg, compopts=None): |
638 """setup core part compression to <alg>""" | 638 """setup core part compression to <alg>""" |
639 if alg in (None, 'UN'): | 639 if alg in (None, b'UN'): |
640 return | 640 return |
641 assert not any(n.lower() == 'compression' for n, v in self._params) | 641 assert not any(n.lower() == b'compression' for n, v in self._params) |
642 self.addparam('Compression', alg) | 642 self.addparam(b'Compression', alg) |
643 self._compengine = util.compengines.forbundletype(alg) | 643 self._compengine = util.compengines.forbundletype(alg) |
644 self._compopts = compopts | 644 self._compopts = compopts |
645 | 645 |
646 @property | 646 @property |
647 def nbparts(self): | 647 def nbparts(self): |
681 return part | 681 return part |
682 | 682 |
683 # methods used to generate the bundle2 stream | 683 # methods used to generate the bundle2 stream |
684 def getchunks(self): | 684 def getchunks(self): |
685 if self.ui.debugflag: | 685 if self.ui.debugflag: |
686 msg = ['bundle2-output-bundle: "%s",' % self._magicstring] | 686 msg = [b'bundle2-output-bundle: "%s",' % self._magicstring] |
687 if self._params: | 687 if self._params: |
688 msg.append(' (%i params)' % len(self._params)) | 688 msg.append(b' (%i params)' % len(self._params)) |
689 msg.append(' %i parts total\n' % len(self._parts)) | 689 msg.append(b' %i parts total\n' % len(self._parts)) |
690 self.ui.debug(''.join(msg)) | 690 self.ui.debug(b''.join(msg)) |
691 outdebug(self.ui, 'start emission of %s stream' % self._magicstring) | 691 outdebug(self.ui, b'start emission of %s stream' % self._magicstring) |
692 yield self._magicstring | 692 yield self._magicstring |
693 param = self._paramchunk() | 693 param = self._paramchunk() |
694 outdebug(self.ui, 'bundle parameter: %s' % param) | 694 outdebug(self.ui, b'bundle parameter: %s' % param) |
695 yield _pack(_fstreamparamsize, len(param)) | 695 yield _pack(_fstreamparamsize, len(param)) |
696 if param: | 696 if param: |
697 yield param | 697 yield param |
698 for chunk in self._compengine.compressstream( | 698 for chunk in self._compengine.compressstream( |
699 self._getcorechunk(), self._compopts | 699 self._getcorechunk(), self._compopts |
705 blocks = [] | 705 blocks = [] |
706 for par, value in self._params: | 706 for par, value in self._params: |
707 par = urlreq.quote(par) | 707 par = urlreq.quote(par) |
708 if value is not None: | 708 if value is not None: |
709 value = urlreq.quote(value) | 709 value = urlreq.quote(value) |
710 par = '%s=%s' % (par, value) | 710 par = b'%s=%s' % (par, value) |
711 blocks.append(par) | 711 blocks.append(par) |
712 return ' '.join(blocks) | 712 return b' '.join(blocks) |
713 | 713 |
714 def _getcorechunk(self): | 714 def _getcorechunk(self): |
715 """yield chunk for the core part of the bundle | 715 """yield chunk for the core part of the bundle |
716 | 716 |
717 (all but headers and parameters)""" | 717 (all but headers and parameters)""" |
718 outdebug(self.ui, 'start of parts') | 718 outdebug(self.ui, b'start of parts') |
719 for part in self._parts: | 719 for part in self._parts: |
720 outdebug(self.ui, 'bundle part: "%s"' % part.type) | 720 outdebug(self.ui, b'bundle part: "%s"' % part.type) |
721 for chunk in part.getchunks(ui=self.ui): | 721 for chunk in part.getchunks(ui=self.ui): |
722 yield chunk | 722 yield chunk |
723 outdebug(self.ui, 'end of bundle') | 723 outdebug(self.ui, b'end of bundle') |
724 yield _pack(_fpartheadersize, 0) | 724 yield _pack(_fpartheadersize, 0) |
725 | 725 |
726 def salvageoutput(self): | 726 def salvageoutput(self): |
727 """return a list with a copy of all output parts in the bundle | 727 """return a list with a copy of all output parts in the bundle |
728 | 728 |
729 This is meant to be used during error handling to make sure we preserve | 729 This is meant to be used during error handling to make sure we preserve |
730 server output""" | 730 server output""" |
731 salvaged = [] | 731 salvaged = [] |
732 for part in self._parts: | 732 for part in self._parts: |
733 if part.type.startswith('output'): | 733 if part.type.startswith(b'output'): |
734 salvaged.append(part.copy()) | 734 salvaged.append(part.copy()) |
735 return salvaged | 735 return salvaged |
736 | 736 |
737 | 737 |
738 class unpackermixin(object): | 738 class unpackermixin(object): |
766 def getunbundler(ui, fp, magicstring=None): | 766 def getunbundler(ui, fp, magicstring=None): |
767 """return a valid unbundler object for a given magicstring""" | 767 """return a valid unbundler object for a given magicstring""" |
768 if magicstring is None: | 768 if magicstring is None: |
769 magicstring = changegroup.readexactly(fp, 4) | 769 magicstring = changegroup.readexactly(fp, 4) |
770 magic, version = magicstring[0:2], magicstring[2:4] | 770 magic, version = magicstring[0:2], magicstring[2:4] |
771 if magic != 'HG': | 771 if magic != b'HG': |
772 ui.debug( | 772 ui.debug( |
773 "error: invalid magic: %r (version %r), should be 'HG'\n" | 773 b"error: invalid magic: %r (version %r), should be 'HG'\n" |
774 % (magic, version) | 774 % (magic, version) |
775 ) | 775 ) |
776 raise error.Abort(_('not a Mercurial bundle')) | 776 raise error.Abort(_(b'not a Mercurial bundle')) |
777 unbundlerclass = formatmap.get(version) | 777 unbundlerclass = formatmap.get(version) |
778 if unbundlerclass is None: | 778 if unbundlerclass is None: |
779 raise error.Abort(_('unknown bundle version %s') % version) | 779 raise error.Abort(_(b'unknown bundle version %s') % version) |
780 unbundler = unbundlerclass(ui, fp) | 780 unbundler = unbundlerclass(ui, fp) |
781 indebug(ui, 'start processing of %s stream' % magicstring) | 781 indebug(ui, b'start processing of %s stream' % magicstring) |
782 return unbundler | 782 return unbundler |
783 | 783 |
784 | 784 |
785 class unbundle20(unpackermixin): | 785 class unbundle20(unpackermixin): |
786 """interpret a bundle2 stream | 786 """interpret a bundle2 stream |
787 | 787 |
788 This class is fed with a binary stream and yields parts through its | 788 This class is fed with a binary stream and yields parts through its |
789 `iterparts` methods.""" | 789 `iterparts` methods.""" |
790 | 790 |
791 _magicstring = 'HG20' | 791 _magicstring = b'HG20' |
792 | 792 |
793 def __init__(self, ui, fp): | 793 def __init__(self, ui, fp): |
794 """If header is specified, we do not read it out of the stream.""" | 794 """If header is specified, we do not read it out of the stream.""" |
795 self.ui = ui | 795 self.ui = ui |
796 self._compengine = util.compengines.forbundletype('UN') | 796 self._compengine = util.compengines.forbundletype(b'UN') |
797 self._compressed = None | 797 self._compressed = None |
798 super(unbundle20, self).__init__(fp) | 798 super(unbundle20, self).__init__(fp) |
799 | 799 |
800 @util.propertycache | 800 @util.propertycache |
801 def params(self): | 801 def params(self): |
802 """dictionary of stream level parameters""" | 802 """dictionary of stream level parameters""" |
803 indebug(self.ui, 'reading bundle2 stream parameters') | 803 indebug(self.ui, b'reading bundle2 stream parameters') |
804 params = {} | 804 params = {} |
805 paramssize = self._unpack(_fstreamparamsize)[0] | 805 paramssize = self._unpack(_fstreamparamsize)[0] |
806 if paramssize < 0: | 806 if paramssize < 0: |
807 raise error.BundleValueError( | 807 raise error.BundleValueError( |
808 'negative bundle param size: %i' % paramssize | 808 b'negative bundle param size: %i' % paramssize |
809 ) | 809 ) |
810 if paramssize: | 810 if paramssize: |
811 params = self._readexact(paramssize) | 811 params = self._readexact(paramssize) |
812 params = self._processallparams(params) | 812 params = self._processallparams(params) |
813 return params | 813 return params |
814 | 814 |
815 def _processallparams(self, paramsblock): | 815 def _processallparams(self, paramsblock): |
816 """""" | 816 """""" |
817 params = util.sortdict() | 817 params = util.sortdict() |
818 for p in paramsblock.split(' '): | 818 for p in paramsblock.split(b' '): |
819 p = p.split('=', 1) | 819 p = p.split(b'=', 1) |
820 p = [urlreq.unquote(i) for i in p] | 820 p = [urlreq.unquote(i) for i in p] |
821 if len(p) < 2: | 821 if len(p) < 2: |
822 p.append(None) | 822 p.append(None) |
823 self._processparam(*p) | 823 self._processparam(*p) |
824 params[p[0]] = p[1] | 824 params[p[0]] = p[1] |
840 raise ValueError(r'non letter first character: %s' % name) | 840 raise ValueError(r'non letter first character: %s' % name) |
841 try: | 841 try: |
842 handler = b2streamparamsmap[name.lower()] | 842 handler = b2streamparamsmap[name.lower()] |
843 except KeyError: | 843 except KeyError: |
844 if name[0:1].islower(): | 844 if name[0:1].islower(): |
845 indebug(self.ui, "ignoring unknown parameter %s" % name) | 845 indebug(self.ui, b"ignoring unknown parameter %s" % name) |
846 else: | 846 else: |
847 raise error.BundleUnknownFeatureError(params=(name,)) | 847 raise error.BundleUnknownFeatureError(params=(name,)) |
848 else: | 848 else: |
849 handler(self, name, value) | 849 handler(self, name, value) |
850 | 850 |
855 have no way to know then the reply end, relying on the bundle to be | 855 have no way to know then the reply end, relying on the bundle to be |
856 interpreted to know its end. This is terrible and we are sorry, but we | 856 interpreted to know its end. This is terrible and we are sorry, but we |
857 needed to move forward to get general delta enabled. | 857 needed to move forward to get general delta enabled. |
858 """ | 858 """ |
859 yield self._magicstring | 859 yield self._magicstring |
860 assert 'params' not in vars(self) | 860 assert b'params' not in vars(self) |
861 paramssize = self._unpack(_fstreamparamsize)[0] | 861 paramssize = self._unpack(_fstreamparamsize)[0] |
862 if paramssize < 0: | 862 if paramssize < 0: |
863 raise error.BundleValueError( | 863 raise error.BundleValueError( |
864 'negative bundle param size: %i' % paramssize | 864 b'negative bundle param size: %i' % paramssize |
865 ) | 865 ) |
866 if paramssize: | 866 if paramssize: |
867 params = self._readexact(paramssize) | 867 params = self._readexact(paramssize) |
868 self._processallparams(params) | 868 self._processallparams(params) |
869 # The payload itself is decompressed below, so drop | 869 # The payload itself is decompressed below, so drop |
870 # the compression parameter passed down to compensate. | 870 # the compression parameter passed down to compensate. |
871 outparams = [] | 871 outparams = [] |
872 for p in params.split(' '): | 872 for p in params.split(b' '): |
873 k, v = p.split('=', 1) | 873 k, v = p.split(b'=', 1) |
874 if k.lower() != 'compression': | 874 if k.lower() != b'compression': |
875 outparams.append(p) | 875 outparams.append(p) |
876 outparams = ' '.join(outparams) | 876 outparams = b' '.join(outparams) |
877 yield _pack(_fstreamparamsize, len(outparams)) | 877 yield _pack(_fstreamparamsize, len(outparams)) |
878 yield outparams | 878 yield outparams |
879 else: | 879 else: |
880 yield _pack(_fstreamparamsize, paramssize) | 880 yield _pack(_fstreamparamsize, paramssize) |
881 # From there, payload might need to be decompressed | 881 # From there, payload might need to be decompressed |
892 emptycount += 1 | 892 emptycount += 1 |
893 continue | 893 continue |
894 if size == flaginterrupt: | 894 if size == flaginterrupt: |
895 continue | 895 continue |
896 elif size < 0: | 896 elif size < 0: |
897 raise error.BundleValueError('negative chunk size: %i') | 897 raise error.BundleValueError(b'negative chunk size: %i') |
898 yield self._readexact(size) | 898 yield self._readexact(size) |
899 | 899 |
900 def iterparts(self, seekable=False): | 900 def iterparts(self, seekable=False): |
901 """yield all parts contained in the stream""" | 901 """yield all parts contained in the stream""" |
902 cls = seekableunbundlepart if seekable else unbundlepart | 902 cls = seekableunbundlepart if seekable else unbundlepart |
903 # make sure param have been loaded | 903 # make sure param have been loaded |
904 self.params | 904 self.params |
905 # From there, payload need to be decompressed | 905 # From there, payload need to be decompressed |
906 self._fp = self._compengine.decompressorreader(self._fp) | 906 self._fp = self._compengine.decompressorreader(self._fp) |
907 indebug(self.ui, 'start extraction of bundle2 parts') | 907 indebug(self.ui, b'start extraction of bundle2 parts') |
908 headerblock = self._readpartheader() | 908 headerblock = self._readpartheader() |
909 while headerblock is not None: | 909 while headerblock is not None: |
910 part = cls(self.ui, headerblock, self._fp) | 910 part = cls(self.ui, headerblock, self._fp) |
911 yield part | 911 yield part |
912 # Ensure part is fully consumed so we can start reading the next | 912 # Ensure part is fully consumed so we can start reading the next |
913 # part. | 913 # part. |
914 part.consume() | 914 part.consume() |
915 | 915 |
916 headerblock = self._readpartheader() | 916 headerblock = self._readpartheader() |
917 indebug(self.ui, 'end of bundle2 stream') | 917 indebug(self.ui, b'end of bundle2 stream') |
918 | 918 |
919 def _readpartheader(self): | 919 def _readpartheader(self): |
920 """reads a part header size and return the bytes blob | 920 """reads a part header size and return the bytes blob |
921 | 921 |
922 returns None if empty""" | 922 returns None if empty""" |
923 headersize = self._unpack(_fpartheadersize)[0] | 923 headersize = self._unpack(_fpartheadersize)[0] |
924 if headersize < 0: | 924 if headersize < 0: |
925 raise error.BundleValueError( | 925 raise error.BundleValueError( |
926 'negative part header size: %i' % headersize | 926 b'negative part header size: %i' % headersize |
927 ) | 927 ) |
928 indebug(self.ui, 'part header size: %i' % headersize) | 928 indebug(self.ui, b'part header size: %i' % headersize) |
929 if headersize: | 929 if headersize: |
930 return self._readexact(headersize) | 930 return self._readexact(headersize) |
931 return None | 931 return None |
932 | 932 |
933 def compressed(self): | 933 def compressed(self): |
934 self.params # load params | 934 self.params # load params |
935 return self._compressed | 935 return self._compressed |
936 | 936 |
937 def close(self): | 937 def close(self): |
938 """close underlying file""" | 938 """close underlying file""" |
939 if util.safehasattr(self._fp, 'close'): | 939 if util.safehasattr(self._fp, b'close'): |
940 return self._fp.close() | 940 return self._fp.close() |
941 | 941 |
942 | 942 |
943 formatmap = {'20': unbundle20} | 943 formatmap = {b'20': unbundle20} |
944 | 944 |
945 b2streamparamsmap = {} | 945 b2streamparamsmap = {} |
946 | 946 |
947 | 947 |
948 def b2streamparamhandler(name): | 948 def b2streamparamhandler(name): |
954 return func | 954 return func |
955 | 955 |
956 return decorator | 956 return decorator |
957 | 957 |
958 | 958 |
959 @b2streamparamhandler('compression') | 959 @b2streamparamhandler(b'compression') |
960 def processcompression(unbundler, param, value): | 960 def processcompression(unbundler, param, value): |
961 """read compression parameter and install payload decompression""" | 961 """read compression parameter and install payload decompression""" |
962 if value not in util.compengines.supportedbundletypes: | 962 if value not in util.compengines.supportedbundletypes: |
963 raise error.BundleUnknownFeatureError(params=(param,), values=(value,)) | 963 raise error.BundleUnknownFeatureError(params=(param,), values=(value,)) |
964 unbundler._compengine = util.compengines.forbundletype(value) | 964 unbundler._compengine = util.compengines.forbundletype(value) |
985 def __init__( | 985 def __init__( |
986 self, | 986 self, |
987 parttype, | 987 parttype, |
988 mandatoryparams=(), | 988 mandatoryparams=(), |
989 advisoryparams=(), | 989 advisoryparams=(), |
990 data='', | 990 data=b'', |
991 mandatory=True, | 991 mandatory=True, |
992 ): | 992 ): |
993 validateparttype(parttype) | 993 validateparttype(parttype) |
994 self.id = None | 994 self.id = None |
995 self.type = parttype | 995 self.type = parttype |
998 self._advisoryparams = list(advisoryparams) | 998 self._advisoryparams = list(advisoryparams) |
999 # checking for duplicated entries | 999 # checking for duplicated entries |
1000 self._seenparams = set() | 1000 self._seenparams = set() |
1001 for pname, __ in self._mandatoryparams + self._advisoryparams: | 1001 for pname, __ in self._mandatoryparams + self._advisoryparams: |
1002 if pname in self._seenparams: | 1002 if pname in self._seenparams: |
1003 raise error.ProgrammingError('duplicated params: %s' % pname) | 1003 raise error.ProgrammingError(b'duplicated params: %s' % pname) |
1004 self._seenparams.add(pname) | 1004 self._seenparams.add(pname) |
1005 # status of the part's generation: | 1005 # status of the part's generation: |
1006 # - None: not started, | 1006 # - None: not started, |
1007 # - False: currently generated, | 1007 # - False: currently generated, |
1008 # - True: generation done. | 1008 # - True: generation done. |
1009 self._generated = None | 1009 self._generated = None |
1010 self.mandatory = mandatory | 1010 self.mandatory = mandatory |
1011 | 1011 |
1012 def __repr__(self): | 1012 def __repr__(self): |
1013 cls = "%s.%s" % (self.__class__.__module__, self.__class__.__name__) | 1013 cls = b"%s.%s" % (self.__class__.__module__, self.__class__.__name__) |
1014 return '<%s object at %x; id: %s; type: %s; mandatory: %s>' % ( | 1014 return b'<%s object at %x; id: %s; type: %s; mandatory: %s>' % ( |
1015 cls, | 1015 cls, |
1016 id(self), | 1016 id(self), |
1017 self.id, | 1017 self.id, |
1018 self.type, | 1018 self.type, |
1019 self.mandatory, | 1019 self.mandatory, |
1022 def copy(self): | 1022 def copy(self): |
1023 """return a copy of the part | 1023 """return a copy of the part |
1024 | 1024 |
1025 The new part have the very same content but no partid assigned yet. | 1025 The new part have the very same content but no partid assigned yet. |
1026 Parts with generated data cannot be copied.""" | 1026 Parts with generated data cannot be copied.""" |
1027 assert not util.safehasattr(self.data, 'next') | 1027 assert not util.safehasattr(self.data, b'next') |
1028 return self.__class__( | 1028 return self.__class__( |
1029 self.type, | 1029 self.type, |
1030 self._mandatoryparams, | 1030 self._mandatoryparams, |
1031 self._advisoryparams, | 1031 self._advisoryparams, |
1032 self._data, | 1032 self._data, |
1039 return self._data | 1039 return self._data |
1040 | 1040 |
1041 @data.setter | 1041 @data.setter |
1042 def data(self, data): | 1042 def data(self, data): |
1043 if self._generated is not None: | 1043 if self._generated is not None: |
1044 raise error.ReadOnlyPartError('part is being generated') | 1044 raise error.ReadOnlyPartError(b'part is being generated') |
1045 self._data = data | 1045 self._data = data |
1046 | 1046 |
1047 @property | 1047 @property |
1048 def mandatoryparams(self): | 1048 def mandatoryparams(self): |
1049 # make it an immutable tuple to force people through ``addparam`` | 1049 # make it an immutable tuple to force people through ``addparam`` |
1052 @property | 1052 @property |
1053 def advisoryparams(self): | 1053 def advisoryparams(self): |
1054 # make it an immutable tuple to force people through ``addparam`` | 1054 # make it an immutable tuple to force people through ``addparam`` |
1055 return tuple(self._advisoryparams) | 1055 return tuple(self._advisoryparams) |
1056 | 1056 |
1057 def addparam(self, name, value='', mandatory=True): | 1057 def addparam(self, name, value=b'', mandatory=True): |
1058 """add a parameter to the part | 1058 """add a parameter to the part |
1059 | 1059 |
1060 If 'mandatory' is set to True, the remote handler must claim support | 1060 If 'mandatory' is set to True, the remote handler must claim support |
1061 for this parameter or the unbundling will be aborted. | 1061 for this parameter or the unbundling will be aborted. |
1062 | 1062 |
1063 The 'name' and 'value' cannot exceed 255 bytes each. | 1063 The 'name' and 'value' cannot exceed 255 bytes each. |
1064 """ | 1064 """ |
1065 if self._generated is not None: | 1065 if self._generated is not None: |
1066 raise error.ReadOnlyPartError('part is being generated') | 1066 raise error.ReadOnlyPartError(b'part is being generated') |
1067 if name in self._seenparams: | 1067 if name in self._seenparams: |
1068 raise ValueError('duplicated params: %s' % name) | 1068 raise ValueError(b'duplicated params: %s' % name) |
1069 self._seenparams.add(name) | 1069 self._seenparams.add(name) |
1070 params = self._advisoryparams | 1070 params = self._advisoryparams |
1071 if mandatory: | 1071 if mandatory: |
1072 params = self._mandatoryparams | 1072 params = self._mandatoryparams |
1073 params.append((name, value)) | 1073 params.append((name, value)) |
1074 | 1074 |
1075 # methods used to generates the bundle2 stream | 1075 # methods used to generates the bundle2 stream |
1076 def getchunks(self, ui): | 1076 def getchunks(self, ui): |
1077 if self._generated is not None: | 1077 if self._generated is not None: |
1078 raise error.ProgrammingError('part can only be consumed once') | 1078 raise error.ProgrammingError(b'part can only be consumed once') |
1079 self._generated = False | 1079 self._generated = False |
1080 | 1080 |
1081 if ui.debugflag: | 1081 if ui.debugflag: |
1082 msg = ['bundle2-output-part: "%s"' % self.type] | 1082 msg = [b'bundle2-output-part: "%s"' % self.type] |
1083 if not self.mandatory: | 1083 if not self.mandatory: |
1084 msg.append(' (advisory)') | 1084 msg.append(b' (advisory)') |
1085 nbmp = len(self.mandatoryparams) | 1085 nbmp = len(self.mandatoryparams) |
1086 nbap = len(self.advisoryparams) | 1086 nbap = len(self.advisoryparams) |
1087 if nbmp or nbap: | 1087 if nbmp or nbap: |
1088 msg.append(' (params:') | 1088 msg.append(b' (params:') |
1089 if nbmp: | 1089 if nbmp: |
1090 msg.append(' %i mandatory' % nbmp) | 1090 msg.append(b' %i mandatory' % nbmp) |
1091 if nbap: | 1091 if nbap: |
1092 msg.append(' %i advisory' % nbmp) | 1092 msg.append(b' %i advisory' % nbmp) |
1093 msg.append(')') | 1093 msg.append(b')') |
1094 if not self.data: | 1094 if not self.data: |
1095 msg.append(' empty payload') | 1095 msg.append(b' empty payload') |
1096 elif util.safehasattr(self.data, 'next') or util.safehasattr( | 1096 elif util.safehasattr(self.data, b'next') or util.safehasattr( |
1097 self.data, '__next__' | 1097 self.data, b'__next__' |
1098 ): | 1098 ): |
1099 msg.append(' streamed payload') | 1099 msg.append(b' streamed payload') |
1100 else: | 1100 else: |
1101 msg.append(' %i bytes payload' % len(self.data)) | 1101 msg.append(b' %i bytes payload' % len(self.data)) |
1102 msg.append('\n') | 1102 msg.append(b'\n') |
1103 ui.debug(''.join(msg)) | 1103 ui.debug(b''.join(msg)) |
1104 | 1104 |
1105 #### header | 1105 #### header |
1106 if self.mandatory: | 1106 if self.mandatory: |
1107 parttype = self.type.upper() | 1107 parttype = self.type.upper() |
1108 else: | 1108 else: |
1109 parttype = self.type.lower() | 1109 parttype = self.type.lower() |
1110 outdebug(ui, 'part %s: "%s"' % (pycompat.bytestr(self.id), parttype)) | 1110 outdebug(ui, b'part %s: "%s"' % (pycompat.bytestr(self.id), parttype)) |
1111 ## parttype | 1111 ## parttype |
1112 header = [ | 1112 header = [ |
1113 _pack(_fparttypesize, len(parttype)), | 1113 _pack(_fparttypesize, len(parttype)), |
1114 parttype, | 1114 parttype, |
1115 _pack(_fpartid, self.id), | 1115 _pack(_fpartid, self.id), |
1136 for key, value in advpar: | 1136 for key, value in advpar: |
1137 header.append(key) | 1137 header.append(key) |
1138 header.append(value) | 1138 header.append(value) |
1139 ## finalize header | 1139 ## finalize header |
1140 try: | 1140 try: |
1141 headerchunk = ''.join(header) | 1141 headerchunk = b''.join(header) |
1142 except TypeError: | 1142 except TypeError: |
1143 raise TypeError( | 1143 raise TypeError( |
1144 r'Found a non-bytes trying to ' | 1144 r'Found a non-bytes trying to ' |
1145 r'build bundle part header: %r' % header | 1145 r'build bundle part header: %r' % header |
1146 ) | 1146 ) |
1147 outdebug(ui, 'header chunk size: %i' % len(headerchunk)) | 1147 outdebug(ui, b'header chunk size: %i' % len(headerchunk)) |
1148 yield _pack(_fpartheadersize, len(headerchunk)) | 1148 yield _pack(_fpartheadersize, len(headerchunk)) |
1149 yield headerchunk | 1149 yield headerchunk |
1150 ## payload | 1150 ## payload |
1151 try: | 1151 try: |
1152 for chunk in self._payloadchunks(): | 1152 for chunk in self._payloadchunks(): |
1153 outdebug(ui, 'payload chunk size: %i' % len(chunk)) | 1153 outdebug(ui, b'payload chunk size: %i' % len(chunk)) |
1154 yield _pack(_fpayloadsize, len(chunk)) | 1154 yield _pack(_fpayloadsize, len(chunk)) |
1155 yield chunk | 1155 yield chunk |
1156 except GeneratorExit: | 1156 except GeneratorExit: |
1157 # GeneratorExit means that nobody is listening for our | 1157 # GeneratorExit means that nobody is listening for our |
1158 # results anyway, so just bail quickly rather than trying | 1158 # results anyway, so just bail quickly rather than trying |
1159 # to produce an error part. | 1159 # to produce an error part. |
1160 ui.debug('bundle2-generatorexit\n') | 1160 ui.debug(b'bundle2-generatorexit\n') |
1161 raise | 1161 raise |
1162 except BaseException as exc: | 1162 except BaseException as exc: |
1163 bexc = stringutil.forcebytestr(exc) | 1163 bexc = stringutil.forcebytestr(exc) |
1164 # backup exception data for later | 1164 # backup exception data for later |
1165 ui.debug( | 1165 ui.debug( |
1166 'bundle2-input-stream-interrupt: encoding exception %s' % bexc | 1166 b'bundle2-input-stream-interrupt: encoding exception %s' % bexc |
1167 ) | 1167 ) |
1168 tb = sys.exc_info()[2] | 1168 tb = sys.exc_info()[2] |
1169 msg = 'unexpected error: %s' % bexc | 1169 msg = b'unexpected error: %s' % bexc |
1170 interpart = bundlepart( | 1170 interpart = bundlepart( |
1171 'error:abort', [('message', msg)], mandatory=False | 1171 b'error:abort', [(b'message', msg)], mandatory=False |
1172 ) | 1172 ) |
1173 interpart.id = 0 | 1173 interpart.id = 0 |
1174 yield _pack(_fpayloadsize, -1) | 1174 yield _pack(_fpayloadsize, -1) |
1175 for chunk in interpart.getchunks(ui=ui): | 1175 for chunk in interpart.getchunks(ui=ui): |
1176 yield chunk | 1176 yield chunk |
1177 outdebug(ui, 'closing payload chunk') | 1177 outdebug(ui, b'closing payload chunk') |
1178 # abort current part payload | 1178 # abort current part payload |
1179 yield _pack(_fpayloadsize, 0) | 1179 yield _pack(_fpayloadsize, 0) |
1180 pycompat.raisewithtb(exc, tb) | 1180 pycompat.raisewithtb(exc, tb) |
1181 # end of payload | 1181 # end of payload |
1182 outdebug(ui, 'closing payload chunk') | 1182 outdebug(ui, b'closing payload chunk') |
1183 yield _pack(_fpayloadsize, 0) | 1183 yield _pack(_fpayloadsize, 0) |
1184 self._generated = True | 1184 self._generated = True |
1185 | 1185 |
1186 def _payloadchunks(self): | 1186 def _payloadchunks(self): |
1187 """yield chunks of a the part payload | 1187 """yield chunks of a the part payload |
1188 | 1188 |
1189 Exists to handle the different methods to provide data to a part.""" | 1189 Exists to handle the different methods to provide data to a part.""" |
1190 # we only support fixed size data now. | 1190 # we only support fixed size data now. |
1191 # This will be improved in the future. | 1191 # This will be improved in the future. |
1192 if util.safehasattr(self.data, 'next') or util.safehasattr( | 1192 if util.safehasattr(self.data, b'next') or util.safehasattr( |
1193 self.data, '__next__' | 1193 self.data, b'__next__' |
1194 ): | 1194 ): |
1195 buff = util.chunkbuffer(self.data) | 1195 buff = util.chunkbuffer(self.data) |
1196 chunk = buff.read(preferedchunksize) | 1196 chunk = buff.read(preferedchunksize) |
1197 while chunk: | 1197 while chunk: |
1198 yield chunk | 1198 yield chunk |
1221 | 1221 |
1222 returns None if empty""" | 1222 returns None if empty""" |
1223 headersize = self._unpack(_fpartheadersize)[0] | 1223 headersize = self._unpack(_fpartheadersize)[0] |
1224 if headersize < 0: | 1224 if headersize < 0: |
1225 raise error.BundleValueError( | 1225 raise error.BundleValueError( |
1226 'negative part header size: %i' % headersize | 1226 b'negative part header size: %i' % headersize |
1227 ) | 1227 ) |
1228 indebug(self.ui, 'part header size: %i\n' % headersize) | 1228 indebug(self.ui, b'part header size: %i\n' % headersize) |
1229 if headersize: | 1229 if headersize: |
1230 return self._readexact(headersize) | 1230 return self._readexact(headersize) |
1231 return None | 1231 return None |
1232 | 1232 |
1233 def __call__(self): | 1233 def __call__(self): |
1234 | 1234 |
1235 self.ui.debug( | 1235 self.ui.debug( |
1236 'bundle2-input-stream-interrupt:' ' opening out of band context\n' | 1236 b'bundle2-input-stream-interrupt:' b' opening out of band context\n' |
1237 ) | 1237 ) |
1238 indebug(self.ui, 'bundle2 stream interruption, looking for a part.') | 1238 indebug(self.ui, b'bundle2 stream interruption, looking for a part.') |
1239 headerblock = self._readpartheader() | 1239 headerblock = self._readpartheader() |
1240 if headerblock is None: | 1240 if headerblock is None: |
1241 indebug(self.ui, 'no part found during interruption.') | 1241 indebug(self.ui, b'no part found during interruption.') |
1242 return | 1242 return |
1243 part = unbundlepart(self.ui, headerblock, self._fp) | 1243 part = unbundlepart(self.ui, headerblock, self._fp) |
1244 op = interruptoperation(self.ui) | 1244 op = interruptoperation(self.ui) |
1245 hardabort = False | 1245 hardabort = False |
1246 try: | 1246 try: |
1250 raise | 1250 raise |
1251 finally: | 1251 finally: |
1252 if not hardabort: | 1252 if not hardabort: |
1253 part.consume() | 1253 part.consume() |
1254 self.ui.debug( | 1254 self.ui.debug( |
1255 'bundle2-input-stream-interrupt:' ' closing out of band context\n' | 1255 b'bundle2-input-stream-interrupt:' b' closing out of band context\n' |
1256 ) | 1256 ) |
1257 | 1257 |
1258 | 1258 |
1259 class interruptoperation(object): | 1259 class interruptoperation(object): |
1260 """A limited operation to be use by part handler during interruption | 1260 """A limited operation to be use by part handler during interruption |
1267 self.reply = None | 1267 self.reply = None |
1268 self.captureoutput = False | 1268 self.captureoutput = False |
1269 | 1269 |
1270 @property | 1270 @property |
1271 def repo(self): | 1271 def repo(self): |
1272 raise error.ProgrammingError('no repo access from stream interruption') | 1272 raise error.ProgrammingError(b'no repo access from stream interruption') |
1273 | 1273 |
1274 def gettransaction(self): | 1274 def gettransaction(self): |
1275 raise TransactionUnavailable('no repo access from stream interruption') | 1275 raise TransactionUnavailable(b'no repo access from stream interruption') |
1276 | 1276 |
1277 | 1277 |
1278 def decodepayloadchunks(ui, fh): | 1278 def decodepayloadchunks(ui, fh): |
1279 """Reads bundle2 part payload data into chunks. | 1279 """Reads bundle2 part payload data into chunks. |
1280 | 1280 |
1281 Part payload data consists of framed chunks. This function takes | 1281 Part payload data consists of framed chunks. This function takes |
1282 a file handle and emits those chunks. | 1282 a file handle and emits those chunks. |
1283 """ | 1283 """ |
1284 dolog = ui.configbool('devel', 'bundle2.debug') | 1284 dolog = ui.configbool(b'devel', b'bundle2.debug') |
1285 debug = ui.debug | 1285 debug = ui.debug |
1286 | 1286 |
1287 headerstruct = struct.Struct(_fpayloadsize) | 1287 headerstruct = struct.Struct(_fpayloadsize) |
1288 headersize = headerstruct.size | 1288 headersize = headerstruct.size |
1289 unpack = headerstruct.unpack | 1289 unpack = headerstruct.unpack |
1290 | 1290 |
1291 readexactly = changegroup.readexactly | 1291 readexactly = changegroup.readexactly |
1292 read = fh.read | 1292 read = fh.read |
1293 | 1293 |
1294 chunksize = unpack(readexactly(fh, headersize))[0] | 1294 chunksize = unpack(readexactly(fh, headersize))[0] |
1295 indebug(ui, 'payload chunk size: %i' % chunksize) | 1295 indebug(ui, b'payload chunk size: %i' % chunksize) |
1296 | 1296 |
1297 # changegroup.readexactly() is inlined below for performance. | 1297 # changegroup.readexactly() is inlined below for performance. |
1298 while chunksize: | 1298 while chunksize: |
1299 if chunksize >= 0: | 1299 if chunksize >= 0: |
1300 s = read(chunksize) | 1300 s = read(chunksize) |
1301 if len(s) < chunksize: | 1301 if len(s) < chunksize: |
1302 raise error.Abort( | 1302 raise error.Abort( |
1303 _( | 1303 _( |
1304 'stream ended unexpectedly ' | 1304 b'stream ended unexpectedly ' |
1305 ' (got %d bytes, expected %d)' | 1305 b' (got %d bytes, expected %d)' |
1306 ) | 1306 ) |
1307 % (len(s), chunksize) | 1307 % (len(s), chunksize) |
1308 ) | 1308 ) |
1309 | 1309 |
1310 yield s | 1310 yield s |
1312 # Interrupt "signal" detected. The regular stream is interrupted | 1312 # Interrupt "signal" detected. The regular stream is interrupted |
1313 # and a bundle2 part follows. Consume it. | 1313 # and a bundle2 part follows. Consume it. |
1314 interrupthandler(ui, fh)() | 1314 interrupthandler(ui, fh)() |
1315 else: | 1315 else: |
1316 raise error.BundleValueError( | 1316 raise error.BundleValueError( |
1317 'negative payload chunk size: %s' % chunksize | 1317 b'negative payload chunk size: %s' % chunksize |
1318 ) | 1318 ) |
1319 | 1319 |
1320 s = read(headersize) | 1320 s = read(headersize) |
1321 if len(s) < headersize: | 1321 if len(s) < headersize: |
1322 raise error.Abort( | 1322 raise error.Abort( |
1323 _('stream ended unexpectedly ' ' (got %d bytes, expected %d)') | 1323 _(b'stream ended unexpectedly ' b' (got %d bytes, expected %d)') |
1324 % (len(s), chunksize) | 1324 % (len(s), chunksize) |
1325 ) | 1325 ) |
1326 | 1326 |
1327 chunksize = unpack(s)[0] | 1327 chunksize = unpack(s)[0] |
1328 | 1328 |
1329 # indebug() inlined for performance. | 1329 # indebug() inlined for performance. |
1330 if dolog: | 1330 if dolog: |
1331 debug('bundle2-input: payload chunk size: %i\n' % chunksize) | 1331 debug(b'bundle2-input: payload chunk size: %i\n' % chunksize) |
1332 | 1332 |
1333 | 1333 |
1334 class unbundlepart(unpackermixin): | 1334 class unbundlepart(unpackermixin): |
1335 """a bundle part read from a bundle""" | 1335 """a bundle part read from a bundle""" |
1336 | 1336 |
1337 def __init__(self, ui, header, fp): | 1337 def __init__(self, ui, header, fp): |
1338 super(unbundlepart, self).__init__(fp) | 1338 super(unbundlepart, self).__init__(fp) |
1339 self._seekable = util.safehasattr(fp, 'seek') and util.safehasattr( | 1339 self._seekable = util.safehasattr(fp, b'seek') and util.safehasattr( |
1340 fp, 'tell' | 1340 fp, b'tell' |
1341 ) | 1341 ) |
1342 self.ui = ui | 1342 self.ui = ui |
1343 # unbundle state attr | 1343 # unbundle state attr |
1344 self._headerdata = header | 1344 self._headerdata = header |
1345 self._headeroffset = 0 | 1345 self._headeroffset = 0 |
1382 | 1382 |
1383 def _readheader(self): | 1383 def _readheader(self): |
1384 """read the header and setup the object""" | 1384 """read the header and setup the object""" |
1385 typesize = self._unpackheader(_fparttypesize)[0] | 1385 typesize = self._unpackheader(_fparttypesize)[0] |
1386 self.type = self._fromheader(typesize) | 1386 self.type = self._fromheader(typesize) |
1387 indebug(self.ui, 'part type: "%s"' % self.type) | 1387 indebug(self.ui, b'part type: "%s"' % self.type) |
1388 self.id = self._unpackheader(_fpartid)[0] | 1388 self.id = self._unpackheader(_fpartid)[0] |
1389 indebug(self.ui, 'part id: "%s"' % pycompat.bytestr(self.id)) | 1389 indebug(self.ui, b'part id: "%s"' % pycompat.bytestr(self.id)) |
1390 # extract mandatory bit from type | 1390 # extract mandatory bit from type |
1391 self.mandatory = self.type != self.type.lower() | 1391 self.mandatory = self.type != self.type.lower() |
1392 self.type = self.type.lower() | 1392 self.type = self.type.lower() |
1393 ## reading parameters | 1393 ## reading parameters |
1394 # param count | 1394 # param count |
1395 mancount, advcount = self._unpackheader(_fpartparamcount) | 1395 mancount, advcount = self._unpackheader(_fpartparamcount) |
1396 indebug(self.ui, 'part parameters: %i' % (mancount + advcount)) | 1396 indebug(self.ui, b'part parameters: %i' % (mancount + advcount)) |
1397 # param size | 1397 # param size |
1398 fparamsizes = _makefpartparamsizes(mancount + advcount) | 1398 fparamsizes = _makefpartparamsizes(mancount + advcount) |
1399 paramsizes = self._unpackheader(fparamsizes) | 1399 paramsizes = self._unpackheader(fparamsizes) |
1400 # make it a list of couple again | 1400 # make it a list of couple again |
1401 paramsizes = list(zip(paramsizes[::2], paramsizes[1::2])) | 1401 paramsizes = list(zip(paramsizes[::2], paramsizes[1::2])) |
1443 data = self._payloadstream.read(size) | 1443 data = self._payloadstream.read(size) |
1444 self._pos += len(data) | 1444 self._pos += len(data) |
1445 if size is None or len(data) < size: | 1445 if size is None or len(data) < size: |
1446 if not self.consumed and self._pos: | 1446 if not self.consumed and self._pos: |
1447 self.ui.debug( | 1447 self.ui.debug( |
1448 'bundle2-input-part: total payload size %i\n' % self._pos | 1448 b'bundle2-input-part: total payload size %i\n' % self._pos |
1449 ) | 1449 ) |
1450 self.consumed = True | 1450 self.consumed = True |
1451 return data | 1451 return data |
1452 | 1452 |
1453 | 1453 |
1476 super(seekableunbundlepart, self).__init__(ui, header, fp) | 1476 super(seekableunbundlepart, self).__init__(ui, header, fp) |
1477 | 1477 |
1478 def _payloadchunks(self, chunknum=0): | 1478 def _payloadchunks(self, chunknum=0): |
1479 '''seek to specified chunk and start yielding data''' | 1479 '''seek to specified chunk and start yielding data''' |
1480 if len(self._chunkindex) == 0: | 1480 if len(self._chunkindex) == 0: |
1481 assert chunknum == 0, 'Must start with chunk 0' | 1481 assert chunknum == 0, b'Must start with chunk 0' |
1482 self._chunkindex.append((0, self._tellfp())) | 1482 self._chunkindex.append((0, self._tellfp())) |
1483 else: | 1483 else: |
1484 assert chunknum < len(self._chunkindex), ( | 1484 assert chunknum < len(self._chunkindex), ( |
1485 'Unknown chunk %d' % chunknum | 1485 b'Unknown chunk %d' % chunknum |
1486 ) | 1486 ) |
1487 self._seekfp(self._chunkindex[chunknum][1]) | 1487 self._seekfp(self._chunkindex[chunknum][1]) |
1488 | 1488 |
1489 pos = self._chunkindex[chunknum][0] | 1489 pos = self._chunkindex[chunknum][0] |
1490 | 1490 |
1501 for chunk, (ppos, fpos) in enumerate(self._chunkindex): | 1501 for chunk, (ppos, fpos) in enumerate(self._chunkindex): |
1502 if ppos == pos: | 1502 if ppos == pos: |
1503 return chunk, 0 | 1503 return chunk, 0 |
1504 elif ppos > pos: | 1504 elif ppos > pos: |
1505 return chunk - 1, pos - self._chunkindex[chunk - 1][0] | 1505 return chunk - 1, pos - self._chunkindex[chunk - 1][0] |
1506 raise ValueError('Unknown chunk') | 1506 raise ValueError(b'Unknown chunk') |
1507 | 1507 |
1508 def tell(self): | 1508 def tell(self): |
1509 return self._pos | 1509 return self._pos |
1510 | 1510 |
1511 def seek(self, offset, whence=os.SEEK_SET): | 1511 def seek(self, offset, whence=os.SEEK_SET): |
1519 chunk = self.read(32768) | 1519 chunk = self.read(32768) |
1520 while chunk: | 1520 while chunk: |
1521 chunk = self.read(32768) | 1521 chunk = self.read(32768) |
1522 newpos = self._chunkindex[-1][0] - offset | 1522 newpos = self._chunkindex[-1][0] - offset |
1523 else: | 1523 else: |
1524 raise ValueError('Unknown whence value: %r' % (whence,)) | 1524 raise ValueError(b'Unknown whence value: %r' % (whence,)) |
1525 | 1525 |
1526 if newpos > self._chunkindex[-1][0] and not self.consumed: | 1526 if newpos > self._chunkindex[-1][0] and not self.consumed: |
1527 # Can't use self.consume() here because it advances self._pos. | 1527 # Can't use self.consume() here because it advances self._pos. |
1528 chunk = self.read(32768) | 1528 chunk = self.read(32768) |
1529 while chunk: | 1529 while chunk: |
1530 chunk = self.read(32668) | 1530 chunk = self.read(32668) |
1531 | 1531 |
1532 if not 0 <= newpos <= self._chunkindex[-1][0]: | 1532 if not 0 <= newpos <= self._chunkindex[-1][0]: |
1533 raise ValueError('Offset out of range') | 1533 raise ValueError(b'Offset out of range') |
1534 | 1534 |
1535 if self._pos != newpos: | 1535 if self._pos != newpos: |
1536 chunk, internaloffset = self._findchunk(newpos) | 1536 chunk, internaloffset = self._findchunk(newpos) |
1537 self._payloadstream = util.chunkbuffer(self._payloadchunks(chunk)) | 1537 self._payloadstream = util.chunkbuffer(self._payloadchunks(chunk)) |
1538 adjust = self.read(internaloffset) | 1538 adjust = self.read(internaloffset) |
1539 if len(adjust) != internaloffset: | 1539 if len(adjust) != internaloffset: |
1540 raise error.Abort(_('Seek failed\n')) | 1540 raise error.Abort(_(b'Seek failed\n')) |
1541 self._pos = newpos | 1541 self._pos = newpos |
1542 | 1542 |
1543 def _seekfp(self, offset, whence=0): | 1543 def _seekfp(self, offset, whence=0): |
1544 """move the underlying file pointer | 1544 """move the underlying file pointer |
1545 | 1545 |
1549 | 1549 |
1550 Do not use it to implement higher-level logic or methods.""" | 1550 Do not use it to implement higher-level logic or methods.""" |
1551 if self._seekable: | 1551 if self._seekable: |
1552 return self._fp.seek(offset, whence) | 1552 return self._fp.seek(offset, whence) |
1553 else: | 1553 else: |
1554 raise NotImplementedError(_('File pointer is not seekable')) | 1554 raise NotImplementedError(_(b'File pointer is not seekable')) |
1555 | 1555 |
1556 def _tellfp(self): | 1556 def _tellfp(self): |
1557 """return the file offset, or None if file is not seekable | 1557 """return the file offset, or None if file is not seekable |
1558 | 1558 |
1559 This method is meant for internal usage by the bundle2 protocol only. | 1559 This method is meant for internal usage by the bundle2 protocol only. |
1573 | 1573 |
1574 | 1574 |
1575 # These are only the static capabilities. | 1575 # These are only the static capabilities. |
1576 # Check the 'getrepocaps' function for the rest. | 1576 # Check the 'getrepocaps' function for the rest. |
1577 capabilities = { | 1577 capabilities = { |
1578 'HG20': (), | 1578 b'HG20': (), |
1579 'bookmarks': (), | 1579 b'bookmarks': (), |
1580 'error': ('abort', 'unsupportedcontent', 'pushraced', 'pushkey'), | 1580 b'error': (b'abort', b'unsupportedcontent', b'pushraced', b'pushkey'), |
1581 'listkeys': (), | 1581 b'listkeys': (), |
1582 'pushkey': (), | 1582 b'pushkey': (), |
1583 'digests': tuple(sorted(util.DIGESTS.keys())), | 1583 b'digests': tuple(sorted(util.DIGESTS.keys())), |
1584 'remote-changegroup': ('http', 'https'), | 1584 b'remote-changegroup': (b'http', b'https'), |
1585 'hgtagsfnodes': (), | 1585 b'hgtagsfnodes': (), |
1586 'rev-branch-cache': (), | 1586 b'rev-branch-cache': (), |
1587 'phases': ('heads',), | 1587 b'phases': (b'heads',), |
1588 'stream': ('v2',), | 1588 b'stream': (b'v2',), |
1589 } | 1589 } |
1590 | 1590 |
1591 | 1591 |
1592 def getrepocaps(repo, allowpushback=False, role=None): | 1592 def getrepocaps(repo, allowpushback=False, role=None): |
1593 """return the bundle2 capabilities for a given repo | 1593 """return the bundle2 capabilities for a given repo |
1596 | 1596 |
1597 The returned value is used for servers advertising their capabilities as | 1597 The returned value is used for servers advertising their capabilities as |
1598 well as clients advertising their capabilities to servers as part of | 1598 well as clients advertising their capabilities to servers as part of |
1599 bundle2 requests. The ``role`` argument specifies which is which. | 1599 bundle2 requests. The ``role`` argument specifies which is which. |
1600 """ | 1600 """ |
1601 if role not in ('client', 'server'): | 1601 if role not in (b'client', b'server'): |
1602 raise error.ProgrammingError('role argument must be client or server') | 1602 raise error.ProgrammingError(b'role argument must be client or server') |
1603 | 1603 |
1604 caps = capabilities.copy() | 1604 caps = capabilities.copy() |
1605 caps['changegroup'] = tuple( | 1605 caps[b'changegroup'] = tuple( |
1606 sorted(changegroup.supportedincomingversions(repo)) | 1606 sorted(changegroup.supportedincomingversions(repo)) |
1607 ) | 1607 ) |
1608 if obsolete.isenabled(repo, obsolete.exchangeopt): | 1608 if obsolete.isenabled(repo, obsolete.exchangeopt): |
1609 supportedformat = tuple('V%i' % v for v in obsolete.formats) | 1609 supportedformat = tuple(b'V%i' % v for v in obsolete.formats) |
1610 caps['obsmarkers'] = supportedformat | 1610 caps[b'obsmarkers'] = supportedformat |
1611 if allowpushback: | 1611 if allowpushback: |
1612 caps['pushback'] = () | 1612 caps[b'pushback'] = () |
1613 cpmode = repo.ui.config('server', 'concurrent-push-mode') | 1613 cpmode = repo.ui.config(b'server', b'concurrent-push-mode') |
1614 if cpmode == 'check-related': | 1614 if cpmode == b'check-related': |
1615 caps['checkheads'] = ('related',) | 1615 caps[b'checkheads'] = (b'related',) |
1616 if 'phases' in repo.ui.configlist('devel', 'legacy.exchange'): | 1616 if b'phases' in repo.ui.configlist(b'devel', b'legacy.exchange'): |
1617 caps.pop('phases') | 1617 caps.pop(b'phases') |
1618 | 1618 |
1619 # Don't advertise stream clone support in server mode if not configured. | 1619 # Don't advertise stream clone support in server mode if not configured. |
1620 if role == 'server': | 1620 if role == b'server': |
1621 streamsupported = repo.ui.configbool( | 1621 streamsupported = repo.ui.configbool( |
1622 'server', 'uncompressed', untrusted=True | 1622 b'server', b'uncompressed', untrusted=True |
1623 ) | 1623 ) |
1624 featuresupported = repo.ui.configbool('server', 'bundle2.stream') | 1624 featuresupported = repo.ui.configbool(b'server', b'bundle2.stream') |
1625 | 1625 |
1626 if not streamsupported or not featuresupported: | 1626 if not streamsupported or not featuresupported: |
1627 caps.pop('stream') | 1627 caps.pop(b'stream') |
1628 # Else always advertise support on client, because payload support | 1628 # Else always advertise support on client, because payload support |
1629 # should always be advertised. | 1629 # should always be advertised. |
1630 | 1630 |
1631 return caps | 1631 return caps |
1632 | 1632 |
1633 | 1633 |
1634 def bundle2caps(remote): | 1634 def bundle2caps(remote): |
1635 """return the bundle capabilities of a peer as dict""" | 1635 """return the bundle capabilities of a peer as dict""" |
1636 raw = remote.capable('bundle2') | 1636 raw = remote.capable(b'bundle2') |
1637 if not raw and raw != '': | 1637 if not raw and raw != b'': |
1638 return {} | 1638 return {} |
1639 capsblob = urlreq.unquote(remote.capable('bundle2')) | 1639 capsblob = urlreq.unquote(remote.capable(b'bundle2')) |
1640 return decodecaps(capsblob) | 1640 return decodecaps(capsblob) |
1641 | 1641 |
1642 | 1642 |
1643 def obsmarkersversion(caps): | 1643 def obsmarkersversion(caps): |
1644 """extract the list of supported obsmarkers versions from a bundle2caps dict | 1644 """extract the list of supported obsmarkers versions from a bundle2caps dict |
1645 """ | 1645 """ |
1646 obscaps = caps.get('obsmarkers', ()) | 1646 obscaps = caps.get(b'obsmarkers', ()) |
1647 return [int(c[1:]) for c in obscaps if c.startswith('V')] | 1647 return [int(c[1:]) for c in obscaps if c.startswith(b'V')] |
1648 | 1648 |
1649 | 1649 |
1650 def writenewbundle( | 1650 def writenewbundle( |
1651 ui, | 1651 ui, |
1652 repo, | 1652 repo, |
1657 opts, | 1657 opts, |
1658 vfs=None, | 1658 vfs=None, |
1659 compression=None, | 1659 compression=None, |
1660 compopts=None, | 1660 compopts=None, |
1661 ): | 1661 ): |
1662 if bundletype.startswith('HG10'): | 1662 if bundletype.startswith(b'HG10'): |
1663 cg = changegroup.makechangegroup(repo, outgoing, '01', source) | 1663 cg = changegroup.makechangegroup(repo, outgoing, b'01', source) |
1664 return writebundle( | 1664 return writebundle( |
1665 ui, | 1665 ui, |
1666 cg, | 1666 cg, |
1667 filename, | 1667 filename, |
1668 bundletype, | 1668 bundletype, |
1669 vfs=vfs, | 1669 vfs=vfs, |
1670 compression=compression, | 1670 compression=compression, |
1671 compopts=compopts, | 1671 compopts=compopts, |
1672 ) | 1672 ) |
1673 elif not bundletype.startswith('HG20'): | 1673 elif not bundletype.startswith(b'HG20'): |
1674 raise error.ProgrammingError('unknown bundle type: %s' % bundletype) | 1674 raise error.ProgrammingError(b'unknown bundle type: %s' % bundletype) |
1675 | 1675 |
1676 caps = {} | 1676 caps = {} |
1677 if 'obsolescence' in opts: | 1677 if b'obsolescence' in opts: |
1678 caps['obsmarkers'] = ('V1',) | 1678 caps[b'obsmarkers'] = (b'V1',) |
1679 bundle = bundle20(ui, caps) | 1679 bundle = bundle20(ui, caps) |
1680 bundle.setcompression(compression, compopts) | 1680 bundle.setcompression(compression, compopts) |
1681 _addpartsfromopts(ui, repo, bundle, source, outgoing, opts) | 1681 _addpartsfromopts(ui, repo, bundle, source, outgoing, opts) |
1682 chunkiter = bundle.getchunks() | 1682 chunkiter = bundle.getchunks() |
1683 | 1683 |
1692 # different right now. So we keep them separated for now for the sake of | 1692 # different right now. So we keep them separated for now for the sake of |
1693 # simplicity. | 1693 # simplicity. |
1694 | 1694 |
1695 # we might not always want a changegroup in such bundle, for example in | 1695 # we might not always want a changegroup in such bundle, for example in |
1696 # stream bundles | 1696 # stream bundles |
1697 if opts.get('changegroup', True): | 1697 if opts.get(b'changegroup', True): |
1698 cgversion = opts.get('cg.version') | 1698 cgversion = opts.get(b'cg.version') |
1699 if cgversion is None: | 1699 if cgversion is None: |
1700 cgversion = changegroup.safeversion(repo) | 1700 cgversion = changegroup.safeversion(repo) |
1701 cg = changegroup.makechangegroup(repo, outgoing, cgversion, source) | 1701 cg = changegroup.makechangegroup(repo, outgoing, cgversion, source) |
1702 part = bundler.newpart('changegroup', data=cg.getchunks()) | 1702 part = bundler.newpart(b'changegroup', data=cg.getchunks()) |
1703 part.addparam('version', cg.version) | 1703 part.addparam(b'version', cg.version) |
1704 if 'clcount' in cg.extras: | 1704 if b'clcount' in cg.extras: |
1705 part.addparam( | 1705 part.addparam( |
1706 'nbchanges', '%d' % cg.extras['clcount'], mandatory=False | 1706 b'nbchanges', b'%d' % cg.extras[b'clcount'], mandatory=False |
1707 ) | 1707 ) |
1708 if opts.get('phases') and repo.revs( | 1708 if opts.get(b'phases') and repo.revs( |
1709 '%ln and secret()', outgoing.missingheads | 1709 b'%ln and secret()', outgoing.missingheads |
1710 ): | 1710 ): |
1711 part.addparam('targetphase', '%d' % phases.secret, mandatory=False) | 1711 part.addparam( |
1712 | 1712 b'targetphase', b'%d' % phases.secret, mandatory=False |
1713 if opts.get('streamv2', False): | 1713 ) |
1714 | |
1715 if opts.get(b'streamv2', False): | |
1714 addpartbundlestream2(bundler, repo, stream=True) | 1716 addpartbundlestream2(bundler, repo, stream=True) |
1715 | 1717 |
1716 if opts.get('tagsfnodescache', True): | 1718 if opts.get(b'tagsfnodescache', True): |
1717 addparttagsfnodescache(repo, bundler, outgoing) | 1719 addparttagsfnodescache(repo, bundler, outgoing) |
1718 | 1720 |
1719 if opts.get('revbranchcache', True): | 1721 if opts.get(b'revbranchcache', True): |
1720 addpartrevbranchcache(repo, bundler, outgoing) | 1722 addpartrevbranchcache(repo, bundler, outgoing) |
1721 | 1723 |
1722 if opts.get('obsolescence', False): | 1724 if opts.get(b'obsolescence', False): |
1723 obsmarkers = repo.obsstore.relevantmarkers(outgoing.missing) | 1725 obsmarkers = repo.obsstore.relevantmarkers(outgoing.missing) |
1724 buildobsmarkerspart(bundler, obsmarkers) | 1726 buildobsmarkerspart(bundler, obsmarkers) |
1725 | 1727 |
1726 if opts.get('phases', False): | 1728 if opts.get(b'phases', False): |
1727 headsbyphase = phases.subsetphaseheads(repo, outgoing.missing) | 1729 headsbyphase = phases.subsetphaseheads(repo, outgoing.missing) |
1728 phasedata = phases.binaryencode(headsbyphase) | 1730 phasedata = phases.binaryencode(headsbyphase) |
1729 bundler.newpart('phase-heads', data=phasedata) | 1731 bundler.newpart(b'phase-heads', data=phasedata) |
1730 | 1732 |
1731 | 1733 |
1732 def addparttagsfnodescache(repo, bundler, outgoing): | 1734 def addparttagsfnodescache(repo, bundler, outgoing): |
1733 # we include the tags fnode cache for the bundle changeset | 1735 # we include the tags fnode cache for the bundle changeset |
1734 # (as an optional parts) | 1736 # (as an optional parts) |
1749 fnode = cache.getfnode(node, computemissing=False) | 1751 fnode = cache.getfnode(node, computemissing=False) |
1750 if fnode is not None: | 1752 if fnode is not None: |
1751 chunks.extend([node, fnode]) | 1753 chunks.extend([node, fnode]) |
1752 | 1754 |
1753 if chunks: | 1755 if chunks: |
1754 bundler.newpart('hgtagsfnodes', data=''.join(chunks)) | 1756 bundler.newpart(b'hgtagsfnodes', data=b''.join(chunks)) |
1755 | 1757 |
1756 | 1758 |
1757 def addpartrevbranchcache(repo, bundler, outgoing): | 1759 def addpartrevbranchcache(repo, bundler, outgoing): |
1758 # we include the rev branch cache for the bundle changeset | 1760 # we include the rev branch cache for the bundle changeset |
1759 # (as an optional parts) | 1761 # (as an optional parts) |
1772 for n in sorted(nodes): | 1774 for n in sorted(nodes): |
1773 yield n | 1775 yield n |
1774 for n in sorted(closed): | 1776 for n in sorted(closed): |
1775 yield n | 1777 yield n |
1776 | 1778 |
1777 bundler.newpart('cache:rev-branch-cache', data=generate(), mandatory=False) | 1779 bundler.newpart(b'cache:rev-branch-cache', data=generate(), mandatory=False) |
1778 | 1780 |
1779 | 1781 |
1780 def _formatrequirementsspec(requirements): | 1782 def _formatrequirementsspec(requirements): |
1781 requirements = [req for req in requirements if req != "shared"] | 1783 requirements = [req for req in requirements if req != b"shared"] |
1782 return urlreq.quote(','.join(sorted(requirements))) | 1784 return urlreq.quote(b','.join(sorted(requirements))) |
1783 | 1785 |
1784 | 1786 |
1785 def _formatrequirementsparams(requirements): | 1787 def _formatrequirementsparams(requirements): |
1786 requirements = _formatrequirementsspec(requirements) | 1788 requirements = _formatrequirementsspec(requirements) |
1787 params = "%s%s" % (urlreq.quote("requirements="), requirements) | 1789 params = b"%s%s" % (urlreq.quote(b"requirements="), requirements) |
1788 return params | 1790 return params |
1789 | 1791 |
1790 | 1792 |
1791 def addpartbundlestream2(bundler, repo, **kwargs): | 1793 def addpartbundlestream2(bundler, repo, **kwargs): |
1792 if not kwargs.get(r'stream', False): | 1794 if not kwargs.get(r'stream', False): |
1793 return | 1795 return |
1794 | 1796 |
1795 if not streamclone.allowservergeneration(repo): | 1797 if not streamclone.allowservergeneration(repo): |
1796 raise error.Abort( | 1798 raise error.Abort( |
1797 _( | 1799 _( |
1798 'stream data requested but server does not allow ' | 1800 b'stream data requested but server does not allow ' |
1799 'this feature' | 1801 b'this feature' |
1800 ), | 1802 ), |
1801 hint=_( | 1803 hint=_( |
1802 'well-behaved clients should not be ' | 1804 b'well-behaved clients should not be ' |
1803 'requesting stream data from servers not ' | 1805 b'requesting stream data from servers not ' |
1804 'advertising it; the client may be buggy' | 1806 b'advertising it; the client may be buggy' |
1805 ), | 1807 ), |
1806 ) | 1808 ) |
1807 | 1809 |
1808 # Stream clones don't compress well. And compression undermines a | 1810 # Stream clones don't compress well. And compression undermines a |
1809 # goal of stream clones, which is to be fast. Communicate the desire | 1811 # goal of stream clones, which is to be fast. Communicate the desire |
1813 # get the includes and excludes | 1815 # get the includes and excludes |
1814 includepats = kwargs.get(r'includepats') | 1816 includepats = kwargs.get(r'includepats') |
1815 excludepats = kwargs.get(r'excludepats') | 1817 excludepats = kwargs.get(r'excludepats') |
1816 | 1818 |
1817 narrowstream = repo.ui.configbool( | 1819 narrowstream = repo.ui.configbool( |
1818 'experimental', 'server.stream-narrow-clones' | 1820 b'experimental', b'server.stream-narrow-clones' |
1819 ) | 1821 ) |
1820 | 1822 |
1821 if (includepats or excludepats) and not narrowstream: | 1823 if (includepats or excludepats) and not narrowstream: |
1822 raise error.Abort(_('server does not support narrow stream clones')) | 1824 raise error.Abort(_(b'server does not support narrow stream clones')) |
1823 | 1825 |
1824 includeobsmarkers = False | 1826 includeobsmarkers = False |
1825 if repo.obsstore: | 1827 if repo.obsstore: |
1826 remoteversions = obsmarkersversion(bundler.capabilities) | 1828 remoteversions = obsmarkersversion(bundler.capabilities) |
1827 if not remoteversions: | 1829 if not remoteversions: |
1828 raise error.Abort( | 1830 raise error.Abort( |
1829 _( | 1831 _( |
1830 'server has obsolescence markers, but client ' | 1832 b'server has obsolescence markers, but client ' |
1831 'cannot receive them via stream clone' | 1833 b'cannot receive them via stream clone' |
1832 ) | 1834 ) |
1833 ) | 1835 ) |
1834 elif repo.obsstore._version in remoteversions: | 1836 elif repo.obsstore._version in remoteversions: |
1835 includeobsmarkers = True | 1837 includeobsmarkers = True |
1836 | 1838 |
1837 filecount, bytecount, it = streamclone.generatev2( | 1839 filecount, bytecount, it = streamclone.generatev2( |
1838 repo, includepats, excludepats, includeobsmarkers | 1840 repo, includepats, excludepats, includeobsmarkers |
1839 ) | 1841 ) |
1840 requirements = _formatrequirementsspec(repo.requirements) | 1842 requirements = _formatrequirementsspec(repo.requirements) |
1841 part = bundler.newpart('stream2', data=it) | 1843 part = bundler.newpart(b'stream2', data=it) |
1842 part.addparam('bytecount', '%d' % bytecount, mandatory=True) | 1844 part.addparam(b'bytecount', b'%d' % bytecount, mandatory=True) |
1843 part.addparam('filecount', '%d' % filecount, mandatory=True) | 1845 part.addparam(b'filecount', b'%d' % filecount, mandatory=True) |
1844 part.addparam('requirements', requirements, mandatory=True) | 1846 part.addparam(b'requirements', requirements, mandatory=True) |
1845 | 1847 |
1846 | 1848 |
1847 def buildobsmarkerspart(bundler, markers): | 1849 def buildobsmarkerspart(bundler, markers): |
1848 """add an obsmarker part to the bundler with <markers> | 1850 """add an obsmarker part to the bundler with <markers> |
1849 | 1851 |
1854 return None | 1856 return None |
1855 | 1857 |
1856 remoteversions = obsmarkersversion(bundler.capabilities) | 1858 remoteversions = obsmarkersversion(bundler.capabilities) |
1857 version = obsolete.commonversion(remoteversions) | 1859 version = obsolete.commonversion(remoteversions) |
1858 if version is None: | 1860 if version is None: |
1859 raise ValueError('bundler does not support common obsmarker format') | 1861 raise ValueError(b'bundler does not support common obsmarker format') |
1860 stream = obsolete.encodemarkers(markers, True, version=version) | 1862 stream = obsolete.encodemarkers(markers, True, version=version) |
1861 return bundler.newpart('obsmarkers', data=stream) | 1863 return bundler.newpart(b'obsmarkers', data=stream) |
1862 | 1864 |
1863 | 1865 |
1864 def writebundle( | 1866 def writebundle( |
1865 ui, cg, filename, bundletype, vfs=None, compression=None, compopts=None | 1867 ui, cg, filename, bundletype, vfs=None, compression=None, compopts=None |
1866 ): | 1868 ): |
1870 If no filename is specified, a temporary file is created. | 1872 If no filename is specified, a temporary file is created. |
1871 bz2 compression can be turned off. | 1873 bz2 compression can be turned off. |
1872 The bundle file will be deleted in case of errors. | 1874 The bundle file will be deleted in case of errors. |
1873 """ | 1875 """ |
1874 | 1876 |
1875 if bundletype == "HG20": | 1877 if bundletype == b"HG20": |
1876 bundle = bundle20(ui) | 1878 bundle = bundle20(ui) |
1877 bundle.setcompression(compression, compopts) | 1879 bundle.setcompression(compression, compopts) |
1878 part = bundle.newpart('changegroup', data=cg.getchunks()) | 1880 part = bundle.newpart(b'changegroup', data=cg.getchunks()) |
1879 part.addparam('version', cg.version) | 1881 part.addparam(b'version', cg.version) |
1880 if 'clcount' in cg.extras: | 1882 if b'clcount' in cg.extras: |
1881 part.addparam( | 1883 part.addparam( |
1882 'nbchanges', '%d' % cg.extras['clcount'], mandatory=False | 1884 b'nbchanges', b'%d' % cg.extras[b'clcount'], mandatory=False |
1883 ) | 1885 ) |
1884 chunkiter = bundle.getchunks() | 1886 chunkiter = bundle.getchunks() |
1885 else: | 1887 else: |
1886 # compression argument is only for the bundle2 case | 1888 # compression argument is only for the bundle2 case |
1887 assert compression is None | 1889 assert compression is None |
1888 if cg.version != '01': | 1890 if cg.version != b'01': |
1889 raise error.Abort( | 1891 raise error.Abort( |
1890 _('old bundle types only supports v1 ' 'changegroups') | 1892 _(b'old bundle types only supports v1 ' b'changegroups') |
1891 ) | 1893 ) |
1892 header, comp = bundletypes[bundletype] | 1894 header, comp = bundletypes[bundletype] |
1893 if comp not in util.compengines.supportedbundletypes: | 1895 if comp not in util.compengines.supportedbundletypes: |
1894 raise error.Abort(_('unknown stream compression type: %s') % comp) | 1896 raise error.Abort(_(b'unknown stream compression type: %s') % comp) |
1895 compengine = util.compengines.forbundletype(comp) | 1897 compengine = util.compengines.forbundletype(comp) |
1896 | 1898 |
1897 def chunkiter(): | 1899 def chunkiter(): |
1898 yield header | 1900 yield header |
1899 for chunk in compengine.compressstream(cg.getchunks(), compopts): | 1901 for chunk in compengine.compressstream(cg.getchunks(), compopts): |
1906 return changegroup.writechunks(ui, chunkiter, filename, vfs=vfs) | 1908 return changegroup.writechunks(ui, chunkiter, filename, vfs=vfs) |
1907 | 1909 |
1908 | 1910 |
1909 def combinechangegroupresults(op): | 1911 def combinechangegroupresults(op): |
1910 """logic to combine 0 or more addchangegroup results into one""" | 1912 """logic to combine 0 or more addchangegroup results into one""" |
1911 results = [r.get('return', 0) for r in op.records['changegroup']] | 1913 results = [r.get(b'return', 0) for r in op.records[b'changegroup']] |
1912 changedheads = 0 | 1914 changedheads = 0 |
1913 result = 1 | 1915 result = 1 |
1914 for ret in results: | 1916 for ret in results: |
1915 # If any changegroup result is 0, return 0 | 1917 # If any changegroup result is 0, return 0 |
1916 if ret == 0: | 1918 if ret == 0: |
1926 result = -1 + changedheads | 1928 result = -1 + changedheads |
1927 return result | 1929 return result |
1928 | 1930 |
1929 | 1931 |
1930 @parthandler( | 1932 @parthandler( |
1931 'changegroup', ('version', 'nbchanges', 'treemanifest', 'targetphase') | 1933 b'changegroup', (b'version', b'nbchanges', b'treemanifest', b'targetphase') |
1932 ) | 1934 ) |
1933 def handlechangegroup(op, inpart): | 1935 def handlechangegroup(op, inpart): |
1934 """apply a changegroup part on the repo | 1936 """apply a changegroup part on the repo |
1935 | 1937 |
1936 This is a very early implementation that will massive rework before being | 1938 This is a very early implementation that will massive rework before being |
1937 inflicted to any end-user. | 1939 inflicted to any end-user. |
1938 """ | 1940 """ |
1939 from . import localrepo | 1941 from . import localrepo |
1940 | 1942 |
1941 tr = op.gettransaction() | 1943 tr = op.gettransaction() |
1942 unpackerversion = inpart.params.get('version', '01') | 1944 unpackerversion = inpart.params.get(b'version', b'01') |
1943 # We should raise an appropriate exception here | 1945 # We should raise an appropriate exception here |
1944 cg = changegroup.getunbundler(unpackerversion, inpart, None) | 1946 cg = changegroup.getunbundler(unpackerversion, inpart, None) |
1945 # the source and url passed here are overwritten by the one contained in | 1947 # the source and url passed here are overwritten by the one contained in |
1946 # the transaction.hookargs argument. So 'bundle2' is a placeholder | 1948 # the transaction.hookargs argument. So 'bundle2' is a placeholder |
1947 nbchangesets = None | 1949 nbchangesets = None |
1948 if 'nbchanges' in inpart.params: | 1950 if b'nbchanges' in inpart.params: |
1949 nbchangesets = int(inpart.params.get('nbchanges')) | 1951 nbchangesets = int(inpart.params.get(b'nbchanges')) |
1950 if ( | 1952 if ( |
1951 'treemanifest' in inpart.params | 1953 b'treemanifest' in inpart.params |
1952 and 'treemanifest' not in op.repo.requirements | 1954 and b'treemanifest' not in op.repo.requirements |
1953 ): | 1955 ): |
1954 if len(op.repo.changelog) != 0: | 1956 if len(op.repo.changelog) != 0: |
1955 raise error.Abort( | 1957 raise error.Abort( |
1956 _( | 1958 _( |
1957 "bundle contains tree manifests, but local repo is " | 1959 b"bundle contains tree manifests, but local repo is " |
1958 "non-empty and does not use tree manifests" | 1960 b"non-empty and does not use tree manifests" |
1959 ) | 1961 ) |
1960 ) | 1962 ) |
1961 op.repo.requirements.add('treemanifest') | 1963 op.repo.requirements.add(b'treemanifest') |
1962 op.repo.svfs.options = localrepo.resolvestorevfsoptions( | 1964 op.repo.svfs.options = localrepo.resolvestorevfsoptions( |
1963 op.repo.ui, op.repo.requirements, op.repo.features | 1965 op.repo.ui, op.repo.requirements, op.repo.features |
1964 ) | 1966 ) |
1965 op.repo._writerequirements() | 1967 op.repo._writerequirements() |
1966 extrakwargs = {} | 1968 extrakwargs = {} |
1967 targetphase = inpart.params.get('targetphase') | 1969 targetphase = inpart.params.get(b'targetphase') |
1968 if targetphase is not None: | 1970 if targetphase is not None: |
1969 extrakwargs[r'targetphase'] = int(targetphase) | 1971 extrakwargs[r'targetphase'] = int(targetphase) |
1970 ret = _processchangegroup( | 1972 ret = _processchangegroup( |
1971 op, | 1973 op, |
1972 cg, | 1974 cg, |
1973 tr, | 1975 tr, |
1974 'bundle2', | 1976 b'bundle2', |
1975 'bundle2', | 1977 b'bundle2', |
1976 expectedtotal=nbchangesets, | 1978 expectedtotal=nbchangesets, |
1977 **extrakwargs | 1979 **extrakwargs |
1978 ) | 1980 ) |
1979 if op.reply is not None: | 1981 if op.reply is not None: |
1980 # This is definitely not the final form of this | 1982 # This is definitely not the final form of this |
1981 # return. But one need to start somewhere. | 1983 # return. But one need to start somewhere. |
1982 part = op.reply.newpart('reply:changegroup', mandatory=False) | 1984 part = op.reply.newpart(b'reply:changegroup', mandatory=False) |
1983 part.addparam( | 1985 part.addparam( |
1984 'in-reply-to', pycompat.bytestr(inpart.id), mandatory=False | 1986 b'in-reply-to', pycompat.bytestr(inpart.id), mandatory=False |
1985 ) | 1987 ) |
1986 part.addparam('return', '%i' % ret, mandatory=False) | 1988 part.addparam(b'return', b'%i' % ret, mandatory=False) |
1987 assert not inpart.read() | 1989 assert not inpart.read() |
1988 | 1990 |
1989 | 1991 |
1990 _remotechangegroupparams = tuple( | 1992 _remotechangegroupparams = tuple( |
1991 ['url', 'size', 'digests'] + ['digest:%s' % k for k in util.DIGESTS.keys()] | 1993 [b'url', b'size', b'digests'] |
1994 + [b'digest:%s' % k for k in util.DIGESTS.keys()] | |
1992 ) | 1995 ) |
1993 | 1996 |
1994 | 1997 |
1995 @parthandler('remote-changegroup', _remotechangegroupparams) | 1998 @parthandler(b'remote-changegroup', _remotechangegroupparams) |
1996 def handleremotechangegroup(op, inpart): | 1999 def handleremotechangegroup(op, inpart): |
1997 """apply a bundle10 on the repo, given an url and validation information | 2000 """apply a bundle10 on the repo, given an url and validation information |
1998 | 2001 |
1999 All the information about the remote bundle to import are given as | 2002 All the information about the remote bundle to import are given as |
2000 parameters. The parameters include: | 2003 parameters. The parameters include: |
2008 the client matches what the server knows about the bundle. | 2011 the client matches what the server knows about the bundle. |
2009 | 2012 |
2010 When multiple digest types are given, all of them are checked. | 2013 When multiple digest types are given, all of them are checked. |
2011 """ | 2014 """ |
2012 try: | 2015 try: |
2013 raw_url = inpart.params['url'] | 2016 raw_url = inpart.params[b'url'] |
2014 except KeyError: | 2017 except KeyError: |
2015 raise error.Abort(_('remote-changegroup: missing "%s" param') % 'url') | 2018 raise error.Abort(_(b'remote-changegroup: missing "%s" param') % b'url') |
2016 parsed_url = util.url(raw_url) | 2019 parsed_url = util.url(raw_url) |
2017 if parsed_url.scheme not in capabilities['remote-changegroup']: | 2020 if parsed_url.scheme not in capabilities[b'remote-changegroup']: |
2018 raise error.Abort( | 2021 raise error.Abort( |
2019 _('remote-changegroup does not support %s urls') % parsed_url.scheme | 2022 _(b'remote-changegroup does not support %s urls') |
2023 % parsed_url.scheme | |
2020 ) | 2024 ) |
2021 | 2025 |
2022 try: | 2026 try: |
2023 size = int(inpart.params['size']) | 2027 size = int(inpart.params[b'size']) |
2024 except ValueError: | 2028 except ValueError: |
2025 raise error.Abort( | 2029 raise error.Abort( |
2026 _('remote-changegroup: invalid value for param "%s"') % 'size' | 2030 _(b'remote-changegroup: invalid value for param "%s"') % b'size' |
2027 ) | 2031 ) |
2028 except KeyError: | 2032 except KeyError: |
2029 raise error.Abort(_('remote-changegroup: missing "%s" param') % 'size') | 2033 raise error.Abort( |
2034 _(b'remote-changegroup: missing "%s" param') % b'size' | |
2035 ) | |
2030 | 2036 |
2031 digests = {} | 2037 digests = {} |
2032 for typ in inpart.params.get('digests', '').split(): | 2038 for typ in inpart.params.get(b'digests', b'').split(): |
2033 param = 'digest:%s' % typ | 2039 param = b'digest:%s' % typ |
2034 try: | 2040 try: |
2035 value = inpart.params[param] | 2041 value = inpart.params[param] |
2036 except KeyError: | 2042 except KeyError: |
2037 raise error.Abort( | 2043 raise error.Abort( |
2038 _('remote-changegroup: missing "%s" param') % param | 2044 _(b'remote-changegroup: missing "%s" param') % param |
2039 ) | 2045 ) |
2040 digests[typ] = value | 2046 digests[typ] = value |
2041 | 2047 |
2042 real_part = util.digestchecker(url.open(op.ui, raw_url), size, digests) | 2048 real_part = util.digestchecker(url.open(op.ui, raw_url), size, digests) |
2043 | 2049 |
2045 from . import exchange | 2051 from . import exchange |
2046 | 2052 |
2047 cg = exchange.readbundle(op.repo.ui, real_part, raw_url) | 2053 cg = exchange.readbundle(op.repo.ui, real_part, raw_url) |
2048 if not isinstance(cg, changegroup.cg1unpacker): | 2054 if not isinstance(cg, changegroup.cg1unpacker): |
2049 raise error.Abort( | 2055 raise error.Abort( |
2050 _('%s: not a bundle version 1.0') % util.hidepassword(raw_url) | 2056 _(b'%s: not a bundle version 1.0') % util.hidepassword(raw_url) |
2051 ) | 2057 ) |
2052 ret = _processchangegroup(op, cg, tr, 'bundle2', 'bundle2') | 2058 ret = _processchangegroup(op, cg, tr, b'bundle2', b'bundle2') |
2053 if op.reply is not None: | 2059 if op.reply is not None: |
2054 # This is definitely not the final form of this | 2060 # This is definitely not the final form of this |
2055 # return. But one need to start somewhere. | 2061 # return. But one need to start somewhere. |
2056 part = op.reply.newpart('reply:changegroup') | 2062 part = op.reply.newpart(b'reply:changegroup') |
2057 part.addparam( | 2063 part.addparam( |
2058 'in-reply-to', pycompat.bytestr(inpart.id), mandatory=False | 2064 b'in-reply-to', pycompat.bytestr(inpart.id), mandatory=False |
2059 ) | 2065 ) |
2060 part.addparam('return', '%i' % ret, mandatory=False) | 2066 part.addparam(b'return', b'%i' % ret, mandatory=False) |
2061 try: | 2067 try: |
2062 real_part.validate() | 2068 real_part.validate() |
2063 except error.Abort as e: | 2069 except error.Abort as e: |
2064 raise error.Abort( | 2070 raise error.Abort( |
2065 _('bundle at %s is corrupted:\n%s') | 2071 _(b'bundle at %s is corrupted:\n%s') |
2066 % (util.hidepassword(raw_url), bytes(e)) | 2072 % (util.hidepassword(raw_url), bytes(e)) |
2067 ) | 2073 ) |
2068 assert not inpart.read() | 2074 assert not inpart.read() |
2069 | 2075 |
2070 | 2076 |
2071 @parthandler('reply:changegroup', ('return', 'in-reply-to')) | 2077 @parthandler(b'reply:changegroup', (b'return', b'in-reply-to')) |
2072 def handlereplychangegroup(op, inpart): | 2078 def handlereplychangegroup(op, inpart): |
2073 ret = int(inpart.params['return']) | 2079 ret = int(inpart.params[b'return']) |
2074 replyto = int(inpart.params['in-reply-to']) | 2080 replyto = int(inpart.params[b'in-reply-to']) |
2075 op.records.add('changegroup', {'return': ret}, replyto) | 2081 op.records.add(b'changegroup', {b'return': ret}, replyto) |
2076 | 2082 |
2077 | 2083 |
2078 @parthandler('check:bookmarks') | 2084 @parthandler(b'check:bookmarks') |
2079 def handlecheckbookmarks(op, inpart): | 2085 def handlecheckbookmarks(op, inpart): |
2080 """check location of bookmarks | 2086 """check location of bookmarks |
2081 | 2087 |
2082 This part is to be used to detect push race regarding bookmark, it | 2088 This part is to be used to detect push race regarding bookmark, it |
2083 contains binary encoded (bookmark, node) tuple. If the local state does | 2089 contains binary encoded (bookmark, node) tuple. If the local state does |
2084 not marks the one in the part, a PushRaced exception is raised | 2090 not marks the one in the part, a PushRaced exception is raised |
2085 """ | 2091 """ |
2086 bookdata = bookmarks.binarydecode(inpart) | 2092 bookdata = bookmarks.binarydecode(inpart) |
2087 | 2093 |
2088 msgstandard = ( | 2094 msgstandard = ( |
2089 'remote repository changed while pushing - please try again ' | 2095 b'remote repository changed while pushing - please try again ' |
2090 '(bookmark "%s" move from %s to %s)' | 2096 b'(bookmark "%s" move from %s to %s)' |
2091 ) | 2097 ) |
2092 msgmissing = ( | 2098 msgmissing = ( |
2093 'remote repository changed while pushing - please try again ' | 2099 b'remote repository changed while pushing - please try again ' |
2094 '(bookmark "%s" is missing, expected %s)' | 2100 b'(bookmark "%s" is missing, expected %s)' |
2095 ) | 2101 ) |
2096 msgexist = ( | 2102 msgexist = ( |
2097 'remote repository changed while pushing - please try again ' | 2103 b'remote repository changed while pushing - please try again ' |
2098 '(bookmark "%s" set on %s, expected missing)' | 2104 b'(bookmark "%s" set on %s, expected missing)' |
2099 ) | 2105 ) |
2100 for book, node in bookdata: | 2106 for book, node in bookdata: |
2101 currentnode = op.repo._bookmarks.get(book) | 2107 currentnode = op.repo._bookmarks.get(book) |
2102 if currentnode != node: | 2108 if currentnode != node: |
2103 if node is None: | 2109 if node is None: |
2111 nodemod.short(currentnode), | 2117 nodemod.short(currentnode), |
2112 ) | 2118 ) |
2113 raise error.PushRaced(finalmsg) | 2119 raise error.PushRaced(finalmsg) |
2114 | 2120 |
2115 | 2121 |
2116 @parthandler('check:heads') | 2122 @parthandler(b'check:heads') |
2117 def handlecheckheads(op, inpart): | 2123 def handlecheckheads(op, inpart): |
2118 """check that head of the repo did not change | 2124 """check that head of the repo did not change |
2119 | 2125 |
2120 This is used to detect a push race when using unbundle. | 2126 This is used to detect a push race when using unbundle. |
2121 This replaces the "heads" argument of unbundle.""" | 2127 This replaces the "heads" argument of unbundle.""" |
2124 while len(h) == 20: | 2130 while len(h) == 20: |
2125 heads.append(h) | 2131 heads.append(h) |
2126 h = inpart.read(20) | 2132 h = inpart.read(20) |
2127 assert not h | 2133 assert not h |
2128 # Trigger a transaction so that we are guaranteed to have the lock now. | 2134 # Trigger a transaction so that we are guaranteed to have the lock now. |
2129 if op.ui.configbool('experimental', 'bundle2lazylocking'): | 2135 if op.ui.configbool(b'experimental', b'bundle2lazylocking'): |
2130 op.gettransaction() | 2136 op.gettransaction() |
2131 if sorted(heads) != sorted(op.repo.heads()): | 2137 if sorted(heads) != sorted(op.repo.heads()): |
2132 raise error.PushRaced( | 2138 raise error.PushRaced( |
2133 'remote repository changed while pushing - ' 'please try again' | 2139 b'remote repository changed while pushing - ' b'please try again' |
2134 ) | 2140 ) |
2135 | 2141 |
2136 | 2142 |
2137 @parthandler('check:updated-heads') | 2143 @parthandler(b'check:updated-heads') |
2138 def handlecheckupdatedheads(op, inpart): | 2144 def handlecheckupdatedheads(op, inpart): |
2139 """check for race on the heads touched by a push | 2145 """check for race on the heads touched by a push |
2140 | 2146 |
2141 This is similar to 'check:heads' but focus on the heads actually updated | 2147 This is similar to 'check:heads' but focus on the heads actually updated |
2142 during the push. If other activities happen on unrelated heads, it is | 2148 during the push. If other activities happen on unrelated heads, it is |
2149 while len(h) == 20: | 2155 while len(h) == 20: |
2150 heads.append(h) | 2156 heads.append(h) |
2151 h = inpart.read(20) | 2157 h = inpart.read(20) |
2152 assert not h | 2158 assert not h |
2153 # trigger a transaction so that we are guaranteed to have the lock now. | 2159 # trigger a transaction so that we are guaranteed to have the lock now. |
2154 if op.ui.configbool('experimental', 'bundle2lazylocking'): | 2160 if op.ui.configbool(b'experimental', b'bundle2lazylocking'): |
2155 op.gettransaction() | 2161 op.gettransaction() |
2156 | 2162 |
2157 currentheads = set() | 2163 currentheads = set() |
2158 for ls in op.repo.branchmap().iterheads(): | 2164 for ls in op.repo.branchmap().iterheads(): |
2159 currentheads.update(ls) | 2165 currentheads.update(ls) |
2160 | 2166 |
2161 for h in heads: | 2167 for h in heads: |
2162 if h not in currentheads: | 2168 if h not in currentheads: |
2163 raise error.PushRaced( | 2169 raise error.PushRaced( |
2164 'remote repository changed while pushing - ' 'please try again' | 2170 b'remote repository changed while pushing - ' |
2171 b'please try again' | |
2165 ) | 2172 ) |
2166 | 2173 |
2167 | 2174 |
2168 @parthandler('check:phases') | 2175 @parthandler(b'check:phases') |
2169 def handlecheckphases(op, inpart): | 2176 def handlecheckphases(op, inpart): |
2170 """check that phase boundaries of the repository did not change | 2177 """check that phase boundaries of the repository did not change |
2171 | 2178 |
2172 This is used to detect a push race. | 2179 This is used to detect a push race. |
2173 """ | 2180 """ |
2174 phasetonodes = phases.binarydecode(inpart) | 2181 phasetonodes = phases.binarydecode(inpart) |
2175 unfi = op.repo.unfiltered() | 2182 unfi = op.repo.unfiltered() |
2176 cl = unfi.changelog | 2183 cl = unfi.changelog |
2177 phasecache = unfi._phasecache | 2184 phasecache = unfi._phasecache |
2178 msg = ( | 2185 msg = ( |
2179 'remote repository changed while pushing - please try again ' | 2186 b'remote repository changed while pushing - please try again ' |
2180 '(%s is %s expected %s)' | 2187 b'(%s is %s expected %s)' |
2181 ) | 2188 ) |
2182 for expectedphase, nodes in enumerate(phasetonodes): | 2189 for expectedphase, nodes in enumerate(phasetonodes): |
2183 for n in nodes: | 2190 for n in nodes: |
2184 actualphase = phasecache.phase(unfi, cl.rev(n)) | 2191 actualphase = phasecache.phase(unfi, cl.rev(n)) |
2185 if actualphase != expectedphase: | 2192 if actualphase != expectedphase: |
2189 phases.phasenames[expectedphase], | 2196 phases.phasenames[expectedphase], |
2190 ) | 2197 ) |
2191 raise error.PushRaced(finalmsg) | 2198 raise error.PushRaced(finalmsg) |
2192 | 2199 |
2193 | 2200 |
2194 @parthandler('output') | 2201 @parthandler(b'output') |
2195 def handleoutput(op, inpart): | 2202 def handleoutput(op, inpart): |
2196 """forward output captured on the server to the client""" | 2203 """forward output captured on the server to the client""" |
2197 for line in inpart.read().splitlines(): | 2204 for line in inpart.read().splitlines(): |
2198 op.ui.status(_('remote: %s\n') % line) | 2205 op.ui.status(_(b'remote: %s\n') % line) |
2199 | 2206 |
2200 | 2207 |
2201 @parthandler('replycaps') | 2208 @parthandler(b'replycaps') |
2202 def handlereplycaps(op, inpart): | 2209 def handlereplycaps(op, inpart): |
2203 """Notify that a reply bundle should be created | 2210 """Notify that a reply bundle should be created |
2204 | 2211 |
2205 The payload contains the capabilities information for the reply""" | 2212 The payload contains the capabilities information for the reply""" |
2206 caps = decodecaps(inpart.read()) | 2213 caps = decodecaps(inpart.read()) |
2210 | 2217 |
2211 class AbortFromPart(error.Abort): | 2218 class AbortFromPart(error.Abort): |
2212 """Sub-class of Abort that denotes an error from a bundle2 part.""" | 2219 """Sub-class of Abort that denotes an error from a bundle2 part.""" |
2213 | 2220 |
2214 | 2221 |
2215 @parthandler('error:abort', ('message', 'hint')) | 2222 @parthandler(b'error:abort', (b'message', b'hint')) |
2216 def handleerrorabort(op, inpart): | 2223 def handleerrorabort(op, inpart): |
2217 """Used to transmit abort error over the wire""" | 2224 """Used to transmit abort error over the wire""" |
2218 raise AbortFromPart( | 2225 raise AbortFromPart( |
2219 inpart.params['message'], hint=inpart.params.get('hint') | 2226 inpart.params[b'message'], hint=inpart.params.get(b'hint') |
2220 ) | 2227 ) |
2221 | 2228 |
2222 | 2229 |
2223 @parthandler( | 2230 @parthandler( |
2224 'error:pushkey', ('namespace', 'key', 'new', 'old', 'ret', 'in-reply-to') | 2231 b'error:pushkey', |
2232 (b'namespace', b'key', b'new', b'old', b'ret', b'in-reply-to'), | |
2225 ) | 2233 ) |
2226 def handleerrorpushkey(op, inpart): | 2234 def handleerrorpushkey(op, inpart): |
2227 """Used to transmit failure of a mandatory pushkey over the wire""" | 2235 """Used to transmit failure of a mandatory pushkey over the wire""" |
2228 kwargs = {} | 2236 kwargs = {} |
2229 for name in ('namespace', 'key', 'new', 'old', 'ret'): | 2237 for name in (b'namespace', b'key', b'new', b'old', b'ret'): |
2230 value = inpart.params.get(name) | 2238 value = inpart.params.get(name) |
2231 if value is not None: | 2239 if value is not None: |
2232 kwargs[name] = value | 2240 kwargs[name] = value |
2233 raise error.PushkeyFailed( | 2241 raise error.PushkeyFailed( |
2234 inpart.params['in-reply-to'], **pycompat.strkwargs(kwargs) | 2242 inpart.params[b'in-reply-to'], **pycompat.strkwargs(kwargs) |
2235 ) | 2243 ) |
2236 | 2244 |
2237 | 2245 |
2238 @parthandler('error:unsupportedcontent', ('parttype', 'params')) | 2246 @parthandler(b'error:unsupportedcontent', (b'parttype', b'params')) |
2239 def handleerrorunsupportedcontent(op, inpart): | 2247 def handleerrorunsupportedcontent(op, inpart): |
2240 """Used to transmit unknown content error over the wire""" | 2248 """Used to transmit unknown content error over the wire""" |
2241 kwargs = {} | 2249 kwargs = {} |
2242 parttype = inpart.params.get('parttype') | 2250 parttype = inpart.params.get(b'parttype') |
2243 if parttype is not None: | 2251 if parttype is not None: |
2244 kwargs['parttype'] = parttype | 2252 kwargs[b'parttype'] = parttype |
2245 params = inpart.params.get('params') | 2253 params = inpart.params.get(b'params') |
2246 if params is not None: | 2254 if params is not None: |
2247 kwargs['params'] = params.split('\0') | 2255 kwargs[b'params'] = params.split(b'\0') |
2248 | 2256 |
2249 raise error.BundleUnknownFeatureError(**pycompat.strkwargs(kwargs)) | 2257 raise error.BundleUnknownFeatureError(**pycompat.strkwargs(kwargs)) |
2250 | 2258 |
2251 | 2259 |
2252 @parthandler('error:pushraced', ('message',)) | 2260 @parthandler(b'error:pushraced', (b'message',)) |
2253 def handleerrorpushraced(op, inpart): | 2261 def handleerrorpushraced(op, inpart): |
2254 """Used to transmit push race error over the wire""" | 2262 """Used to transmit push race error over the wire""" |
2255 raise error.ResponseError(_('push failed:'), inpart.params['message']) | 2263 raise error.ResponseError(_(b'push failed:'), inpart.params[b'message']) |
2256 | 2264 |
2257 | 2265 |
2258 @parthandler('listkeys', ('namespace',)) | 2266 @parthandler(b'listkeys', (b'namespace',)) |
2259 def handlelistkeys(op, inpart): | 2267 def handlelistkeys(op, inpart): |
2260 """retrieve pushkey namespace content stored in a bundle2""" | 2268 """retrieve pushkey namespace content stored in a bundle2""" |
2261 namespace = inpart.params['namespace'] | 2269 namespace = inpart.params[b'namespace'] |
2262 r = pushkey.decodekeys(inpart.read()) | 2270 r = pushkey.decodekeys(inpart.read()) |
2263 op.records.add('listkeys', (namespace, r)) | 2271 op.records.add(b'listkeys', (namespace, r)) |
2264 | 2272 |
2265 | 2273 |
2266 @parthandler('pushkey', ('namespace', 'key', 'old', 'new')) | 2274 @parthandler(b'pushkey', (b'namespace', b'key', b'old', b'new')) |
2267 def handlepushkey(op, inpart): | 2275 def handlepushkey(op, inpart): |
2268 """process a pushkey request""" | 2276 """process a pushkey request""" |
2269 dec = pushkey.decode | 2277 dec = pushkey.decode |
2270 namespace = dec(inpart.params['namespace']) | 2278 namespace = dec(inpart.params[b'namespace']) |
2271 key = dec(inpart.params['key']) | 2279 key = dec(inpart.params[b'key']) |
2272 old = dec(inpart.params['old']) | 2280 old = dec(inpart.params[b'old']) |
2273 new = dec(inpart.params['new']) | 2281 new = dec(inpart.params[b'new']) |
2274 # Grab the transaction to ensure that we have the lock before performing the | 2282 # Grab the transaction to ensure that we have the lock before performing the |
2275 # pushkey. | 2283 # pushkey. |
2276 if op.ui.configbool('experimental', 'bundle2lazylocking'): | 2284 if op.ui.configbool(b'experimental', b'bundle2lazylocking'): |
2277 op.gettransaction() | 2285 op.gettransaction() |
2278 ret = op.repo.pushkey(namespace, key, old, new) | 2286 ret = op.repo.pushkey(namespace, key, old, new) |
2279 record = {'namespace': namespace, 'key': key, 'old': old, 'new': new} | 2287 record = {b'namespace': namespace, b'key': key, b'old': old, b'new': new} |
2280 op.records.add('pushkey', record) | 2288 op.records.add(b'pushkey', record) |
2281 if op.reply is not None: | 2289 if op.reply is not None: |
2282 rpart = op.reply.newpart('reply:pushkey') | 2290 rpart = op.reply.newpart(b'reply:pushkey') |
2283 rpart.addparam( | 2291 rpart.addparam( |
2284 'in-reply-to', pycompat.bytestr(inpart.id), mandatory=False | 2292 b'in-reply-to', pycompat.bytestr(inpart.id), mandatory=False |
2285 ) | 2293 ) |
2286 rpart.addparam('return', '%i' % ret, mandatory=False) | 2294 rpart.addparam(b'return', b'%i' % ret, mandatory=False) |
2287 if inpart.mandatory and not ret: | 2295 if inpart.mandatory and not ret: |
2288 kwargs = {} | 2296 kwargs = {} |
2289 for key in ('namespace', 'key', 'new', 'old', 'ret'): | 2297 for key in (b'namespace', b'key', b'new', b'old', b'ret'): |
2290 if key in inpart.params: | 2298 if key in inpart.params: |
2291 kwargs[key] = inpart.params[key] | 2299 kwargs[key] = inpart.params[key] |
2292 raise error.PushkeyFailed( | 2300 raise error.PushkeyFailed( |
2293 partid='%d' % inpart.id, **pycompat.strkwargs(kwargs) | 2301 partid=b'%d' % inpart.id, **pycompat.strkwargs(kwargs) |
2294 ) | 2302 ) |
2295 | 2303 |
2296 | 2304 |
2297 @parthandler('bookmarks') | 2305 @parthandler(b'bookmarks') |
2298 def handlebookmark(op, inpart): | 2306 def handlebookmark(op, inpart): |
2299 """transmit bookmark information | 2307 """transmit bookmark information |
2300 | 2308 |
2301 The part contains binary encoded bookmark information. | 2309 The part contains binary encoded bookmark information. |
2302 | 2310 |
2311 When mode is 'records', the information is recorded into the 'bookmarks' | 2319 When mode is 'records', the information is recorded into the 'bookmarks' |
2312 records of the bundle operation. This behavior is suitable for pulling. | 2320 records of the bundle operation. This behavior is suitable for pulling. |
2313 """ | 2321 """ |
2314 changes = bookmarks.binarydecode(inpart) | 2322 changes = bookmarks.binarydecode(inpart) |
2315 | 2323 |
2316 pushkeycompat = op.repo.ui.configbool('server', 'bookmarks-pushkey-compat') | 2324 pushkeycompat = op.repo.ui.configbool( |
2317 bookmarksmode = op.modes.get('bookmarks', 'apply') | 2325 b'server', b'bookmarks-pushkey-compat' |
2318 | 2326 ) |
2319 if bookmarksmode == 'apply': | 2327 bookmarksmode = op.modes.get(b'bookmarks', b'apply') |
2328 | |
2329 if bookmarksmode == b'apply': | |
2320 tr = op.gettransaction() | 2330 tr = op.gettransaction() |
2321 bookstore = op.repo._bookmarks | 2331 bookstore = op.repo._bookmarks |
2322 if pushkeycompat: | 2332 if pushkeycompat: |
2323 allhooks = [] | 2333 allhooks = [] |
2324 for book, node in changes: | 2334 for book, node in changes: |
2325 hookargs = tr.hookargs.copy() | 2335 hookargs = tr.hookargs.copy() |
2326 hookargs['pushkeycompat'] = '1' | 2336 hookargs[b'pushkeycompat'] = b'1' |
2327 hookargs['namespace'] = 'bookmarks' | 2337 hookargs[b'namespace'] = b'bookmarks' |
2328 hookargs['key'] = book | 2338 hookargs[b'key'] = book |
2329 hookargs['old'] = nodemod.hex(bookstore.get(book, '')) | 2339 hookargs[b'old'] = nodemod.hex(bookstore.get(book, b'')) |
2330 hookargs['new'] = nodemod.hex(node if node is not None else '') | 2340 hookargs[b'new'] = nodemod.hex( |
2341 node if node is not None else b'' | |
2342 ) | |
2331 allhooks.append(hookargs) | 2343 allhooks.append(hookargs) |
2332 | 2344 |
2333 for hookargs in allhooks: | 2345 for hookargs in allhooks: |
2334 op.repo.hook( | 2346 op.repo.hook( |
2335 'prepushkey', throw=True, **pycompat.strkwargs(hookargs) | 2347 b'prepushkey', throw=True, **pycompat.strkwargs(hookargs) |
2336 ) | 2348 ) |
2337 | 2349 |
2338 bookstore.applychanges(op.repo, op.gettransaction(), changes) | 2350 bookstore.applychanges(op.repo, op.gettransaction(), changes) |
2339 | 2351 |
2340 if pushkeycompat: | 2352 if pushkeycompat: |
2341 | 2353 |
2342 def runhook(): | 2354 def runhook(): |
2343 for hookargs in allhooks: | 2355 for hookargs in allhooks: |
2344 op.repo.hook('pushkey', **pycompat.strkwargs(hookargs)) | 2356 op.repo.hook(b'pushkey', **pycompat.strkwargs(hookargs)) |
2345 | 2357 |
2346 op.repo._afterlock(runhook) | 2358 op.repo._afterlock(runhook) |
2347 | 2359 |
2348 elif bookmarksmode == 'records': | 2360 elif bookmarksmode == b'records': |
2349 for book, node in changes: | 2361 for book, node in changes: |
2350 record = {'bookmark': book, 'node': node} | 2362 record = {b'bookmark': book, b'node': node} |
2351 op.records.add('bookmarks', record) | 2363 op.records.add(b'bookmarks', record) |
2352 else: | 2364 else: |
2353 raise error.ProgrammingError('unkown bookmark mode: %s' % bookmarksmode) | 2365 raise error.ProgrammingError( |
2354 | 2366 b'unkown bookmark mode: %s' % bookmarksmode |
2355 | 2367 ) |
2356 @parthandler('phase-heads') | 2368 |
2369 | |
2370 @parthandler(b'phase-heads') | |
2357 def handlephases(op, inpart): | 2371 def handlephases(op, inpart): |
2358 """apply phases from bundle part to repo""" | 2372 """apply phases from bundle part to repo""" |
2359 headsbyphase = phases.binarydecode(inpart) | 2373 headsbyphase = phases.binarydecode(inpart) |
2360 phases.updatephases(op.repo.unfiltered(), op.gettransaction, headsbyphase) | 2374 phases.updatephases(op.repo.unfiltered(), op.gettransaction, headsbyphase) |
2361 | 2375 |
2362 | 2376 |
2363 @parthandler('reply:pushkey', ('return', 'in-reply-to')) | 2377 @parthandler(b'reply:pushkey', (b'return', b'in-reply-to')) |
2364 def handlepushkeyreply(op, inpart): | 2378 def handlepushkeyreply(op, inpart): |
2365 """retrieve the result of a pushkey request""" | 2379 """retrieve the result of a pushkey request""" |
2366 ret = int(inpart.params['return']) | 2380 ret = int(inpart.params[b'return']) |
2367 partid = int(inpart.params['in-reply-to']) | 2381 partid = int(inpart.params[b'in-reply-to']) |
2368 op.records.add('pushkey', {'return': ret}, partid) | 2382 op.records.add(b'pushkey', {b'return': ret}, partid) |
2369 | 2383 |
2370 | 2384 |
2371 @parthandler('obsmarkers') | 2385 @parthandler(b'obsmarkers') |
2372 def handleobsmarker(op, inpart): | 2386 def handleobsmarker(op, inpart): |
2373 """add a stream of obsmarkers to the repo""" | 2387 """add a stream of obsmarkers to the repo""" |
2374 tr = op.gettransaction() | 2388 tr = op.gettransaction() |
2375 markerdata = inpart.read() | 2389 markerdata = inpart.read() |
2376 if op.ui.config('experimental', 'obsmarkers-exchange-debug'): | 2390 if op.ui.config(b'experimental', b'obsmarkers-exchange-debug'): |
2377 op.ui.write('obsmarker-exchange: %i bytes received\n' % len(markerdata)) | 2391 op.ui.write( |
2392 b'obsmarker-exchange: %i bytes received\n' % len(markerdata) | |
2393 ) | |
2378 # The mergemarkers call will crash if marker creation is not enabled. | 2394 # The mergemarkers call will crash if marker creation is not enabled. |
2379 # we want to avoid this if the part is advisory. | 2395 # we want to avoid this if the part is advisory. |
2380 if not inpart.mandatory and op.repo.obsstore.readonly: | 2396 if not inpart.mandatory and op.repo.obsstore.readonly: |
2381 op.repo.ui.debug('ignoring obsolescence markers, feature not enabled\n') | 2397 op.repo.ui.debug( |
2398 b'ignoring obsolescence markers, feature not enabled\n' | |
2399 ) | |
2382 return | 2400 return |
2383 new = op.repo.obsstore.mergemarkers(tr, markerdata) | 2401 new = op.repo.obsstore.mergemarkers(tr, markerdata) |
2384 op.repo.invalidatevolatilesets() | 2402 op.repo.invalidatevolatilesets() |
2385 op.records.add('obsmarkers', {'new': new}) | 2403 op.records.add(b'obsmarkers', {b'new': new}) |
2386 if op.reply is not None: | 2404 if op.reply is not None: |
2387 rpart = op.reply.newpart('reply:obsmarkers') | 2405 rpart = op.reply.newpart(b'reply:obsmarkers') |
2388 rpart.addparam( | 2406 rpart.addparam( |
2389 'in-reply-to', pycompat.bytestr(inpart.id), mandatory=False | 2407 b'in-reply-to', pycompat.bytestr(inpart.id), mandatory=False |
2390 ) | 2408 ) |
2391 rpart.addparam('new', '%i' % new, mandatory=False) | 2409 rpart.addparam(b'new', b'%i' % new, mandatory=False) |
2392 | 2410 |
2393 | 2411 |
2394 @parthandler('reply:obsmarkers', ('new', 'in-reply-to')) | 2412 @parthandler(b'reply:obsmarkers', (b'new', b'in-reply-to')) |
2395 def handleobsmarkerreply(op, inpart): | 2413 def handleobsmarkerreply(op, inpart): |
2396 """retrieve the result of a pushkey request""" | 2414 """retrieve the result of a pushkey request""" |
2397 ret = int(inpart.params['new']) | 2415 ret = int(inpart.params[b'new']) |
2398 partid = int(inpart.params['in-reply-to']) | 2416 partid = int(inpart.params[b'in-reply-to']) |
2399 op.records.add('obsmarkers', {'new': ret}, partid) | 2417 op.records.add(b'obsmarkers', {b'new': ret}, partid) |
2400 | 2418 |
2401 | 2419 |
2402 @parthandler('hgtagsfnodes') | 2420 @parthandler(b'hgtagsfnodes') |
2403 def handlehgtagsfnodes(op, inpart): | 2421 def handlehgtagsfnodes(op, inpart): |
2404 """Applies .hgtags fnodes cache entries to the local repo. | 2422 """Applies .hgtags fnodes cache entries to the local repo. |
2405 | 2423 |
2406 Payload is pairs of 20 byte changeset nodes and filenodes. | 2424 Payload is pairs of 20 byte changeset nodes and filenodes. |
2407 """ | 2425 """ |
2408 # Grab the transaction so we ensure that we have the lock at this point. | 2426 # Grab the transaction so we ensure that we have the lock at this point. |
2409 if op.ui.configbool('experimental', 'bundle2lazylocking'): | 2427 if op.ui.configbool(b'experimental', b'bundle2lazylocking'): |
2410 op.gettransaction() | 2428 op.gettransaction() |
2411 cache = tags.hgtagsfnodescache(op.repo.unfiltered()) | 2429 cache = tags.hgtagsfnodescache(op.repo.unfiltered()) |
2412 | 2430 |
2413 count = 0 | 2431 count = 0 |
2414 while True: | 2432 while True: |
2415 node = inpart.read(20) | 2433 node = inpart.read(20) |
2416 fnode = inpart.read(20) | 2434 fnode = inpart.read(20) |
2417 if len(node) < 20 or len(fnode) < 20: | 2435 if len(node) < 20 or len(fnode) < 20: |
2418 op.ui.debug('ignoring incomplete received .hgtags fnodes data\n') | 2436 op.ui.debug(b'ignoring incomplete received .hgtags fnodes data\n') |
2419 break | 2437 break |
2420 cache.setfnode(node, fnode) | 2438 cache.setfnode(node, fnode) |
2421 count += 1 | 2439 count += 1 |
2422 | 2440 |
2423 cache.write() | 2441 cache.write() |
2424 op.ui.debug('applied %i hgtags fnodes cache entries\n' % count) | 2442 op.ui.debug(b'applied %i hgtags fnodes cache entries\n' % count) |
2425 | 2443 |
2426 | 2444 |
2427 rbcstruct = struct.Struct('>III') | 2445 rbcstruct = struct.Struct(b'>III') |
2428 | 2446 |
2429 | 2447 |
2430 @parthandler('cache:rev-branch-cache') | 2448 @parthandler(b'cache:rev-branch-cache') |
2431 def handlerbc(op, inpart): | 2449 def handlerbc(op, inpart): |
2432 """receive a rev-branch-cache payload and update the local cache | 2450 """receive a rev-branch-cache payload and update the local cache |
2433 | 2451 |
2434 The payload is a series of data related to each branch | 2452 The payload is a series of data related to each branch |
2435 | 2453 |
2458 cache.setdata(branch, rev, node, True) | 2476 cache.setdata(branch, rev, node, True) |
2459 rawheader = inpart.read(rbcstruct.size) | 2477 rawheader = inpart.read(rbcstruct.size) |
2460 cache.write() | 2478 cache.write() |
2461 | 2479 |
2462 | 2480 |
2463 @parthandler('pushvars') | 2481 @parthandler(b'pushvars') |
2464 def bundle2getvars(op, part): | 2482 def bundle2getvars(op, part): |
2465 '''unbundle a bundle2 containing shellvars on the server''' | 2483 '''unbundle a bundle2 containing shellvars on the server''' |
2466 # An option to disable unbundling on server-side for security reasons | 2484 # An option to disable unbundling on server-side for security reasons |
2467 if op.ui.configbool('push', 'pushvars.server'): | 2485 if op.ui.configbool(b'push', b'pushvars.server'): |
2468 hookargs = {} | 2486 hookargs = {} |
2469 for key, value in part.advisoryparams: | 2487 for key, value in part.advisoryparams: |
2470 key = key.upper() | 2488 key = key.upper() |
2471 # We want pushed variables to have USERVAR_ prepended so we know | 2489 # We want pushed variables to have USERVAR_ prepended so we know |
2472 # they came from the --pushvar flag. | 2490 # they came from the --pushvar flag. |
2473 key = "USERVAR_" + key | 2491 key = b"USERVAR_" + key |
2474 hookargs[key] = value | 2492 hookargs[key] = value |
2475 op.addhookargs(hookargs) | 2493 op.addhookargs(hookargs) |
2476 | 2494 |
2477 | 2495 |
2478 @parthandler('stream2', ('requirements', 'filecount', 'bytecount')) | 2496 @parthandler(b'stream2', (b'requirements', b'filecount', b'bytecount')) |
2479 def handlestreamv2bundle(op, part): | 2497 def handlestreamv2bundle(op, part): |
2480 | 2498 |
2481 requirements = urlreq.unquote(part.params['requirements']).split(',') | 2499 requirements = urlreq.unquote(part.params[b'requirements']).split(b',') |
2482 filecount = int(part.params['filecount']) | 2500 filecount = int(part.params[b'filecount']) |
2483 bytecount = int(part.params['bytecount']) | 2501 bytecount = int(part.params[b'bytecount']) |
2484 | 2502 |
2485 repo = op.repo | 2503 repo = op.repo |
2486 if len(repo): | 2504 if len(repo): |
2487 msg = _('cannot apply stream clone to non empty repository') | 2505 msg = _(b'cannot apply stream clone to non empty repository') |
2488 raise error.Abort(msg) | 2506 raise error.Abort(msg) |
2489 | 2507 |
2490 repo.ui.debug('applying stream bundle\n') | 2508 repo.ui.debug(b'applying stream bundle\n') |
2491 streamclone.applybundlev2(repo, part, filecount, bytecount, requirements) | 2509 streamclone.applybundlev2(repo, part, filecount, bytecount, requirements) |
2492 | 2510 |
2493 | 2511 |
2494 def widen_bundle( | 2512 def widen_bundle( |
2495 bundler, repo, oldmatcher, newmatcher, common, known, cgversion, ellipses | 2513 bundler, repo, oldmatcher, newmatcher, common, known, cgversion, ellipses |
2507 | 2525 |
2508 returns bundle2 of the data required for extending | 2526 returns bundle2 of the data required for extending |
2509 """ | 2527 """ |
2510 commonnodes = set() | 2528 commonnodes = set() |
2511 cl = repo.changelog | 2529 cl = repo.changelog |
2512 for r in repo.revs("::%ln", common): | 2530 for r in repo.revs(b"::%ln", common): |
2513 commonnodes.add(cl.node(r)) | 2531 commonnodes.add(cl.node(r)) |
2514 if commonnodes: | 2532 if commonnodes: |
2515 # XXX: we should only send the filelogs (and treemanifest). user | 2533 # XXX: we should only send the filelogs (and treemanifest). user |
2516 # already has the changelog and manifest | 2534 # already has the changelog and manifest |
2517 packer = changegroup.getbundler( | 2535 packer = changegroup.getbundler( |
2523 ) | 2541 ) |
2524 cgdata = packer.generate( | 2542 cgdata = packer.generate( |
2525 {nodemod.nullid}, | 2543 {nodemod.nullid}, |
2526 list(commonnodes), | 2544 list(commonnodes), |
2527 False, | 2545 False, |
2528 'narrow_widen', | 2546 b'narrow_widen', |
2529 changelog=False, | 2547 changelog=False, |
2530 ) | 2548 ) |
2531 | 2549 |
2532 part = bundler.newpart('changegroup', data=cgdata) | 2550 part = bundler.newpart(b'changegroup', data=cgdata) |
2533 part.addparam('version', cgversion) | 2551 part.addparam(b'version', cgversion) |
2534 if 'treemanifest' in repo.requirements: | 2552 if b'treemanifest' in repo.requirements: |
2535 part.addparam('treemanifest', '1') | 2553 part.addparam(b'treemanifest', b'1') |
2536 | 2554 |
2537 return bundler | 2555 return bundler |