comparison mercurial/bundle2.py @ 43076:2372284d9457

formatting: blacken the codebase This is using my patch to black (https://github.com/psf/black/pull/826) so we don't un-wrap collection literals. Done with: hg files 'set:**.py - mercurial/thirdparty/** - "contrib/python-zstandard/**"' | xargs black -S # skip-blame mass-reformatting only # no-check-commit reformats foo_bar functions Differential Revision: https://phab.mercurial-scm.org/D6971
author Augie Fackler <augie@google.com>
date Sun, 06 Oct 2019 09:45:02 -0400
parents 181ee2118a96
children 687b865b95ad
comparison
equal deleted inserted replaced
43075:57875cf423c9 43076:2372284d9457
169 streamclone, 169 streamclone,
170 tags, 170 tags,
171 url, 171 url,
172 util, 172 util,
173 ) 173 )
174 from .utils import ( 174 from .utils import stringutil
175 stringutil,
176 )
177 175
178 urlerr = util.urlerr 176 urlerr = util.urlerr
179 urlreq = util.urlreq 177 urlreq = util.urlreq
180 178
181 _pack = struct.pack 179 _pack = struct.pack
190 188
191 preferedchunksize = 32768 189 preferedchunksize = 32768
192 190
193 _parttypeforbidden = re.compile('[^a-zA-Z0-9_:-]') 191 _parttypeforbidden = re.compile('[^a-zA-Z0-9_:-]')
194 192
193
195 def outdebug(ui, message): 194 def outdebug(ui, message):
196 """debug regarding output stream (bundling)""" 195 """debug regarding output stream (bundling)"""
197 if ui.configbool('devel', 'bundle2.debug'): 196 if ui.configbool('devel', 'bundle2.debug'):
198 ui.debug('bundle2-output: %s\n' % message) 197 ui.debug('bundle2-output: %s\n' % message)
199 198
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('devel', 'bundle2.debug'):
203 ui.debug('bundle2-input: %s\n' % message) 203 ui.debug('bundle2-input: %s\n' % message)
204 204
205
205 def validateparttype(parttype): 206 def validateparttype(parttype):
206 """raise ValueError if a parttype contains invalid character""" 207 """raise ValueError if a parttype contains invalid character"""
207 if _parttypeforbidden.search(parttype): 208 if _parttypeforbidden.search(parttype):
208 raise ValueError(parttype) 209 raise ValueError(parttype)
209 210
211
210 def _makefpartparamsizes(nbparams): 212 def _makefpartparamsizes(nbparams):
211 """return a struct format to read part parameter sizes 213 """return a struct format to read part parameter sizes
212 214
213 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
214 dynamically. 216 dynamically.
215 """ 217 """
216 return '>'+('BB'*nbparams) 218 return '>' + ('BB' * nbparams)
219
217 220
218 parthandlermapping = {} 221 parthandlermapping = {}
222
219 223
220 def parthandler(parttype, params=()): 224 def parthandler(parttype, params=()):
221 """decorator that register a function as a bundle2 part handler 225 """decorator that register a function as a bundle2 part handler
222 226
223 eg:: 227 eg::
226 def myparttypehandler(...): 230 def myparttypehandler(...):
227 '''process a part of type "my part".''' 231 '''process a part of type "my part".'''
228 ... 232 ...
229 """ 233 """
230 validateparttype(parttype) 234 validateparttype(parttype)
235
231 def _decorator(func): 236 def _decorator(func):
232 lparttype = parttype.lower() # enforce lower case matching. 237 lparttype = parttype.lower() # enforce lower case matching.
233 assert lparttype not in parthandlermapping 238 assert lparttype not in parthandlermapping
234 parthandlermapping[lparttype] = func 239 parthandlermapping[lparttype] = func
235 func.params = frozenset(params) 240 func.params = frozenset(params)
236 return func 241 return func
242
237 return _decorator 243 return _decorator
244
238 245
239 class unbundlerecords(object): 246 class unbundlerecords(object):
240 """keep record of what happens during and unbundle 247 """keep record of what happens during and unbundle
241 248
242 New records are added using `records.add('cat', obj)`. Where 'cat' is a 249 New records are added using `records.add('cat', obj)`. Where 'cat' is a
280 287
281 def __nonzero__(self): 288 def __nonzero__(self):
282 return bool(self._sequences) 289 return bool(self._sequences)
283 290
284 __bool__ = __nonzero__ 291 __bool__ = __nonzero__
292
285 293
286 class bundleoperation(object): 294 class bundleoperation(object):
287 """an object that represents a single bundling process 295 """an object that represents a single bundling process
288 296
289 Its purpose is to carry unbundle-related objects and states. 297 Its purpose is to carry unbundle-related objects and states.
326 334
327 return transaction 335 return transaction
328 336
329 def addhookargs(self, hookargs): 337 def addhookargs(self, hookargs):
330 if self.hookargs is None: 338 if self.hookargs is None:
331 raise error.ProgrammingError('attempted to add hookargs to ' 339 raise error.ProgrammingError(
332 'operation after transaction started') 340 'attempted to add hookargs to '
341 'operation after transaction started'
342 )
333 self.hookargs.update(hookargs) 343 self.hookargs.update(hookargs)
344
334 345
335 class TransactionUnavailable(RuntimeError): 346 class TransactionUnavailable(RuntimeError):
336 pass 347 pass
348
337 349
338 def _notransaction(): 350 def _notransaction():
339 """default method to get a transaction while processing a bundle 351 """default method to get a transaction while processing a bundle
340 352
341 Raise an exception to highlight the fact that no transaction was expected 353 Raise an exception to highlight the fact that no transaction was expected
342 to be created""" 354 to be created"""
343 raise TransactionUnavailable() 355 raise TransactionUnavailable()
356
344 357
345 def applybundle(repo, unbundler, tr, source, url=None, **kwargs): 358 def applybundle(repo, unbundler, tr, source, url=None, **kwargs):
346 # 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
347 if isinstance(unbundler, unbundle20): 360 if isinstance(unbundler, unbundle20):
348 tr.hookargs['bundle2'] = '1' 361 tr.hookargs['bundle2'] = '1'
355 # 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
356 op = bundleoperation(repo, lambda: tr, source=source) 369 op = bundleoperation(repo, lambda: tr, source=source)
357 _processchangegroup(op, unbundler, tr, source, url, **kwargs) 370 _processchangegroup(op, unbundler, tr, source, url, **kwargs)
358 return op 371 return op
359 372
373
360 class partiterator(object): 374 class partiterator(object):
361 def __init__(self, repo, op, unbundler): 375 def __init__(self, repo, op, unbundler):
362 self.repo = repo 376 self.repo = repo
363 self.op = op 377 self.op = op
364 self.unbundler = unbundler 378 self.unbundler = unbundler
373 self.count = count 387 self.count = count
374 self.current = p 388 self.current = p
375 yield p 389 yield p
376 p.consume() 390 p.consume()
377 self.current = None 391 self.current = None
392
378 self.iterator = func() 393 self.iterator = func()
379 return self.iterator 394 return self.iterator
380 395
381 def __exit__(self, type, exc, tb): 396 def __exit__(self, type, exc, tb):
382 if not self.iterator: 397 if not self.iterator:
420 # Re-raising from a variable loses the original stack. So only use 435 # Re-raising from a variable loses the original stack. So only use
421 # that form if we need to. 436 # that form if we need to.
422 if seekerror: 437 if seekerror:
423 raise exc 438 raise exc
424 439
425 self.repo.ui.debug('bundle2-input-bundle: %i parts total\n' % 440 self.repo.ui.debug(
426 self.count) 441 'bundle2-input-bundle: %i parts total\n' % self.count
442 )
443
427 444
428 def processbundle(repo, unbundler, transactiongetter=None, op=None, source=''): 445 def processbundle(repo, unbundler, transactiongetter=None, op=None, source=''):
429 """This function process a bundle, apply effect to/from a repo 446 """This function process a bundle, apply effect to/from a repo
430 447
431 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
459 476
460 processparts(repo, op, unbundler) 477 processparts(repo, op, unbundler)
461 478
462 return op 479 return op
463 480
481
464 def processparts(repo, op, unbundler): 482 def processparts(repo, op, unbundler):
465 with partiterator(repo, op, unbundler) as parts: 483 with partiterator(repo, op, unbundler) as parts:
466 for part in parts: 484 for part in parts:
467 _processpart(op, part) 485 _processpart(op, part)
468 486
487
469 def _processchangegroup(op, cg, tr, source, url, **kwargs): 488 def _processchangegroup(op, cg, tr, source, url, **kwargs):
470 ret = cg.apply(op.repo, tr, source, url, **kwargs) 489 ret = cg.apply(op.repo, tr, source, url, **kwargs)
471 op.records.add('changegroup', { 490 op.records.add('changegroup', {'return': ret,})
472 'return': ret,
473 })
474 return ret 491 return ret
475 492
493
476 def _gethandler(op, part): 494 def _gethandler(op, part):
477 status = 'unknown' # used by debug output 495 status = 'unknown' # used by debug output
478 try: 496 try:
479 handler = parthandlermapping.get(part.type) 497 handler = parthandlermapping.get(part.type)
480 if handler is None: 498 if handler is None:
481 status = 'unsupported-type' 499 status = 'unsupported-type'
482 raise error.BundleUnknownFeatureError(parttype=part.type) 500 raise error.BundleUnknownFeatureError(parttype=part.type)
484 unknownparams = part.mandatorykeys - handler.params 502 unknownparams = part.mandatorykeys - handler.params
485 if unknownparams: 503 if unknownparams:
486 unknownparams = list(unknownparams) 504 unknownparams = list(unknownparams)
487 unknownparams.sort() 505 unknownparams.sort()
488 status = 'unsupported-params (%s)' % ', '.join(unknownparams) 506 status = 'unsupported-params (%s)' % ', '.join(unknownparams)
489 raise error.BundleUnknownFeatureError(parttype=part.type, 507 raise error.BundleUnknownFeatureError(
490 params=unknownparams) 508 parttype=part.type, params=unknownparams
509 )
491 status = 'supported' 510 status = 'supported'
492 except error.BundleUnknownFeatureError as exc: 511 except error.BundleUnknownFeatureError as exc:
493 if part.mandatory: # mandatory parts 512 if part.mandatory: # mandatory parts
494 raise 513 raise
495 indebug(op.ui, 'ignoring unsupported advisory part %s' % exc) 514 indebug(op.ui, 'ignoring unsupported advisory part %s' % exc)
496 return # skip to part processing 515 return # skip to part processing
497 finally: 516 finally:
498 if op.ui.debugflag: 517 if op.ui.debugflag:
499 msg = ['bundle2-input-part: "%s"' % part.type] 518 msg = ['bundle2-input-part: "%s"' % part.type]
500 if not part.mandatory: 519 if not part.mandatory:
501 msg.append(' (advisory)') 520 msg.append(' (advisory)')
511 msg.append(' %s\n' % status) 530 msg.append(' %s\n' % status)
512 op.ui.debug(''.join(msg)) 531 op.ui.debug(''.join(msg))
513 532
514 return handler 533 return handler
515 534
535
516 def _processpart(op, part): 536 def _processpart(op, part):
517 """process a single part from a bundle 537 """process a single part from a bundle
518 538
519 The part is guaranteed to have been fully consumed when the function exits 539 The part is guaranteed to have been fully consumed when the function exits
520 (even if an exception is raised).""" 540 (even if an exception is raised)."""
534 handler(op, part) 554 handler(op, part)
535 finally: 555 finally:
536 if output is not None: 556 if output is not None:
537 output = op.ui.popbuffer() 557 output = op.ui.popbuffer()
538 if output: 558 if output:
539 outpart = op.reply.newpart('output', data=output, 559 outpart = op.reply.newpart('output', data=output, mandatory=False)
540 mandatory=False)
541 outpart.addparam( 560 outpart.addparam(
542 'in-reply-to', pycompat.bytestr(part.id), mandatory=False) 561 'in-reply-to', pycompat.bytestr(part.id), mandatory=False
562 )
563
543 564
544 def decodecaps(blob): 565 def decodecaps(blob):
545 """decode a bundle2 caps bytes blob into a dictionary 566 """decode a bundle2 caps bytes blob into a dictionary
546 567
547 The blob is a list of capabilities (one per line) 568 The blob is a list of capabilities (one per line)
562 key = urlreq.unquote(key) 583 key = urlreq.unquote(key)
563 vals = [urlreq.unquote(v) for v in vals] 584 vals = [urlreq.unquote(v) for v in vals]
564 caps[key] = vals 585 caps[key] = vals
565 return caps 586 return caps
566 587
588
567 def encodecaps(caps): 589 def encodecaps(caps):
568 """encode a bundle2 caps dictionary into a bytes blob""" 590 """encode a bundle2 caps dictionary into a bytes blob"""
569 chunks = [] 591 chunks = []
570 for ca in sorted(caps): 592 for ca in sorted(caps):
571 vals = caps[ca] 593 vals = caps[ca]
574 if vals: 596 if vals:
575 ca = "%s=%s" % (ca, ','.join(vals)) 597 ca = "%s=%s" % (ca, ','.join(vals))
576 chunks.append(ca) 598 chunks.append(ca)
577 return '\n'.join(chunks) 599 return '\n'.join(chunks)
578 600
601
579 bundletypes = { 602 bundletypes = {
580 "": ("", 'UN'), # only when using unbundle on ssh and old http servers 603 "": ("", 'UN'), # only when using unbundle on ssh and old http servers
581 # since the unification ssh accepts a header but there 604 # since the unification ssh accepts a header but there
582 # is no capability signaling it. 605 # is no capability signaling it.
583 "HG20": (), # special-cased below 606 "HG20": (), # special-cased below
584 "HG10UN": ("HG10UN", 'UN'), 607 "HG10UN": ("HG10UN", 'UN'),
585 "HG10BZ": ("HG10", 'BZ'), 608 "HG10BZ": ("HG10", 'BZ'),
586 "HG10GZ": ("HG10GZ", 'GZ'), 609 "HG10GZ": ("HG10GZ", 'GZ'),
587 } 610 }
588 611
589 # hgweb uses this list to communicate its preferred type 612 # hgweb uses this list to communicate its preferred type
590 bundlepriority = ['HG10GZ', 'HG10BZ', 'HG10UN'] 613 bundlepriority = ['HG10GZ', 'HG10BZ', 'HG10UN']
614
591 615
592 class bundle20(object): 616 class bundle20(object):
593 """represent an outgoing bundle2 container 617 """represent an outgoing bundle2 container
594 618
595 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
628 def addparam(self, name, value=None): 652 def addparam(self, name, value=None):
629 """add a stream level parameter""" 653 """add a stream level parameter"""
630 if not name: 654 if not name:
631 raise error.ProgrammingError(b'empty parameter name') 655 raise error.ProgrammingError(b'empty parameter name')
632 if name[0:1] not in pycompat.bytestr(string.ascii_letters): 656 if name[0:1] not in pycompat.bytestr(string.ascii_letters):
633 raise error.ProgrammingError(b'non letter first character: %s' 657 raise error.ProgrammingError(
634 % name) 658 b'non letter first character: %s' % name
659 )
635 self._params.append((name, value)) 660 self._params.append((name, value))
636 661
637 def addpart(self, part): 662 def addpart(self, part):
638 """add a new part to the bundle2 container 663 """add a new part to the bundle2 container
639 664
640 Parts contains the actual applicative payload.""" 665 Parts contains the actual applicative payload."""
641 assert part.id is None 666 assert part.id is None
642 part.id = len(self._parts) # very cheap counter 667 part.id = len(self._parts) # very cheap counter
643 self._parts.append(part) 668 self._parts.append(part)
644 669
645 def newpart(self, typeid, *args, **kwargs): 670 def newpart(self, typeid, *args, **kwargs):
646 """create a new part and add it to the containers 671 """create a new part and add it to the containers
647 672
668 param = self._paramchunk() 693 param = self._paramchunk()
669 outdebug(self.ui, 'bundle parameter: %s' % param) 694 outdebug(self.ui, 'bundle parameter: %s' % param)
670 yield _pack(_fstreamparamsize, len(param)) 695 yield _pack(_fstreamparamsize, len(param))
671 if param: 696 if param:
672 yield param 697 yield param
673 for chunk in self._compengine.compressstream(self._getcorechunk(), 698 for chunk in self._compengine.compressstream(
674 self._compopts): 699 self._getcorechunk(), self._compopts
700 ):
675 yield chunk 701 yield chunk
676 702
677 def _paramchunk(self): 703 def _paramchunk(self):
678 """return a encoded version of all stream parameters""" 704 """return a encoded version of all stream parameters"""
679 blocks = [] 705 blocks = []
695 for chunk in part.getchunks(ui=self.ui): 721 for chunk in part.getchunks(ui=self.ui):
696 yield chunk 722 yield chunk
697 outdebug(self.ui, 'end of bundle') 723 outdebug(self.ui, 'end of bundle')
698 yield _pack(_fpartheadersize, 0) 724 yield _pack(_fpartheadersize, 0)
699 725
700
701 def salvageoutput(self): 726 def salvageoutput(self):
702 """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
703 728
704 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
705 server output""" 730 server output"""
734 They directly manipulate the low level stream including bundle2 level 759 They directly manipulate the low level stream including bundle2 level
735 instruction. 760 instruction.
736 761
737 Do not use it to implement higher-level logic or methods.""" 762 Do not use it to implement higher-level logic or methods."""
738 return changegroup.readexactly(self._fp, size) 763 return changegroup.readexactly(self._fp, size)
764
739 765
740 def getunbundler(ui, fp, magicstring=None): 766 def getunbundler(ui, fp, magicstring=None):
741 """return a valid unbundler object for a given magicstring""" 767 """return a valid unbundler object for a given magicstring"""
742 if magicstring is None: 768 if magicstring is None:
743 magicstring = changegroup.readexactly(fp, 4) 769 magicstring = changegroup.readexactly(fp, 4)
744 magic, version = magicstring[0:2], magicstring[2:4] 770 magic, version = magicstring[0:2], magicstring[2:4]
745 if magic != 'HG': 771 if magic != 'HG':
746 ui.debug( 772 ui.debug(
747 "error: invalid magic: %r (version %r), should be 'HG'\n" 773 "error: invalid magic: %r (version %r), should be 'HG'\n"
748 % (magic, version)) 774 % (magic, version)
775 )
749 raise error.Abort(_('not a Mercurial bundle')) 776 raise error.Abort(_('not a Mercurial bundle'))
750 unbundlerclass = formatmap.get(version) 777 unbundlerclass = formatmap.get(version)
751 if unbundlerclass is None: 778 if unbundlerclass is None:
752 raise error.Abort(_('unknown bundle version %s') % version) 779 raise error.Abort(_('unknown bundle version %s') % version)
753 unbundler = unbundlerclass(ui, fp) 780 unbundler = unbundlerclass(ui, fp)
754 indebug(ui, 'start processing of %s stream' % magicstring) 781 indebug(ui, 'start processing of %s stream' % magicstring)
755 return unbundler 782 return unbundler
756 783
784
757 class unbundle20(unpackermixin): 785 class unbundle20(unpackermixin):
758 """interpret a bundle2 stream 786 """interpret a bundle2 stream
759 787
760 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
761 `iterparts` methods.""" 789 `iterparts` methods."""
774 """dictionary of stream level parameters""" 802 """dictionary of stream level parameters"""
775 indebug(self.ui, 'reading bundle2 stream parameters') 803 indebug(self.ui, 'reading bundle2 stream parameters')
776 params = {} 804 params = {}
777 paramssize = self._unpack(_fstreamparamsize)[0] 805 paramssize = self._unpack(_fstreamparamsize)[0]
778 if paramssize < 0: 806 if paramssize < 0:
779 raise error.BundleValueError('negative bundle param size: %i' 807 raise error.BundleValueError(
780 % paramssize) 808 'negative bundle param size: %i' % paramssize
809 )
781 if paramssize: 810 if paramssize:
782 params = self._readexact(paramssize) 811 params = self._readexact(paramssize)
783 params = self._processallparams(params) 812 params = self._processallparams(params)
784 return params 813 return params
785 814
792 if len(p) < 2: 821 if len(p) < 2:
793 p.append(None) 822 p.append(None)
794 self._processparam(*p) 823 self._processparam(*p)
795 params[p[0]] = p[1] 824 params[p[0]] = p[1]
796 return params 825 return params
797
798 826
799 def _processparam(self, name, value): 827 def _processparam(self, name, value):
800 """process a parameter, applying its effect if needed 828 """process a parameter, applying its effect if needed
801 829
802 Parameter starting with a lower case letter are advisory and will be 830 Parameter starting with a lower case letter are advisory and will be
830 """ 858 """
831 yield self._magicstring 859 yield self._magicstring
832 assert 'params' not in vars(self) 860 assert 'params' not in vars(self)
833 paramssize = self._unpack(_fstreamparamsize)[0] 861 paramssize = self._unpack(_fstreamparamsize)[0]
834 if paramssize < 0: 862 if paramssize < 0:
835 raise error.BundleValueError('negative bundle param size: %i' 863 raise error.BundleValueError(
836 % paramssize) 864 'negative bundle param size: %i' % paramssize
865 )
837 if paramssize: 866 if paramssize:
838 params = self._readexact(paramssize) 867 params = self._readexact(paramssize)
839 self._processallparams(params) 868 self._processallparams(params)
840 # The payload itself is decompressed below, so drop 869 # The payload itself is decompressed below, so drop
841 # the compression parameter passed down to compensate. 870 # the compression parameter passed down to compensate.
866 continue 895 continue
867 elif size < 0: 896 elif size < 0:
868 raise error.BundleValueError('negative chunk size: %i') 897 raise error.BundleValueError('negative chunk size: %i')
869 yield self._readexact(size) 898 yield self._readexact(size)
870 899
871
872 def iterparts(self, seekable=False): 900 def iterparts(self, seekable=False):
873 """yield all parts contained in the stream""" 901 """yield all parts contained in the stream"""
874 cls = seekableunbundlepart if seekable else unbundlepart 902 cls = seekableunbundlepart if seekable else unbundlepart
875 # make sure param have been loaded 903 # make sure param have been loaded
876 self.params 904 self.params
892 """reads a part header size and return the bytes blob 920 """reads a part header size and return the bytes blob
893 921
894 returns None if empty""" 922 returns None if empty"""
895 headersize = self._unpack(_fpartheadersize)[0] 923 headersize = self._unpack(_fpartheadersize)[0]
896 if headersize < 0: 924 if headersize < 0:
897 raise error.BundleValueError('negative part header size: %i' 925 raise error.BundleValueError(
898 % headersize) 926 'negative part header size: %i' % headersize
927 )
899 indebug(self.ui, 'part header size: %i' % headersize) 928 indebug(self.ui, 'part header size: %i' % headersize)
900 if headersize: 929 if headersize:
901 return self._readexact(headersize) 930 return self._readexact(headersize)
902 return None 931 return None
903 932
904 def compressed(self): 933 def compressed(self):
905 self.params # load params 934 self.params # load params
906 return self._compressed 935 return self._compressed
907 936
908 def close(self): 937 def close(self):
909 """close underlying file""" 938 """close underlying file"""
910 if util.safehasattr(self._fp, 'close'): 939 if util.safehasattr(self._fp, 'close'):
911 return self._fp.close() 940 return self._fp.close()
912 941
942
913 formatmap = {'20': unbundle20} 943 formatmap = {'20': unbundle20}
914 944
915 b2streamparamsmap = {} 945 b2streamparamsmap = {}
946
916 947
917 def b2streamparamhandler(name): 948 def b2streamparamhandler(name):
918 """register a handler for a stream level parameter""" 949 """register a handler for a stream level parameter"""
950
919 def decorator(func): 951 def decorator(func):
920 assert name not in formatmap 952 assert name not in formatmap
921 b2streamparamsmap[name] = func 953 b2streamparamsmap[name] = func
922 return func 954 return func
955
923 return decorator 956 return decorator
957
924 958
925 @b2streamparamhandler('compression') 959 @b2streamparamhandler('compression')
926 def processcompression(unbundler, param, value): 960 def processcompression(unbundler, param, value):
927 """read compression parameter and install payload decompression""" 961 """read compression parameter and install payload decompression"""
928 if value not in util.compengines.supportedbundletypes: 962 if value not in util.compengines.supportedbundletypes:
929 raise error.BundleUnknownFeatureError(params=(param,), 963 raise error.BundleUnknownFeatureError(params=(param,), values=(value,))
930 values=(value,))
931 unbundler._compengine = util.compengines.forbundletype(value) 964 unbundler._compengine = util.compengines.forbundletype(value)
932 if value is not None: 965 if value is not None:
933 unbundler._compressed = True 966 unbundler._compressed = True
934 967
968
935 class bundlepart(object): 969 class bundlepart(object):
936 """A bundle2 part contains application level payload 970 """A bundle2 part contains application level payload
937 971
938 The part `type` is used to route the part to the application level 972 The part `type` is used to route the part to the application level
939 handler. 973 handler.
946 should be able to safely ignore the advisory ones. 980 should be able to safely ignore the advisory ones.
947 981
948 Both data and parameters cannot be modified after the generation has begun. 982 Both data and parameters cannot be modified after the generation has begun.
949 """ 983 """
950 984
951 def __init__(self, parttype, mandatoryparams=(), advisoryparams=(), 985 def __init__(
952 data='', mandatory=True): 986 self,
987 parttype,
988 mandatoryparams=(),
989 advisoryparams=(),
990 data='',
991 mandatory=True,
992 ):
953 validateparttype(parttype) 993 validateparttype(parttype)
954 self.id = None 994 self.id = None
955 self.type = parttype 995 self.type = parttype
956 self._data = data 996 self._data = data
957 self._mandatoryparams = list(mandatoryparams) 997 self._mandatoryparams = list(mandatoryparams)
969 self._generated = None 1009 self._generated = None
970 self.mandatory = mandatory 1010 self.mandatory = mandatory
971 1011
972 def __repr__(self): 1012 def __repr__(self):
973 cls = "%s.%s" % (self.__class__.__module__, self.__class__.__name__) 1013 cls = "%s.%s" % (self.__class__.__module__, self.__class__.__name__)
974 return ('<%s object at %x; id: %s; type: %s; mandatory: %s>' 1014 return '<%s object at %x; id: %s; type: %s; mandatory: %s>' % (
975 % (cls, id(self), self.id, self.type, self.mandatory)) 1015 cls,
1016 id(self),
1017 self.id,
1018 self.type,
1019 self.mandatory,
1020 )
976 1021
977 def copy(self): 1022 def copy(self):
978 """return a copy of the part 1023 """return a copy of the part
979 1024
980 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.
981 Parts with generated data cannot be copied.""" 1026 Parts with generated data cannot be copied."""
982 assert not util.safehasattr(self.data, 'next') 1027 assert not util.safehasattr(self.data, 'next')
983 return self.__class__(self.type, self._mandatoryparams, 1028 return self.__class__(
984 self._advisoryparams, self._data, self.mandatory) 1029 self.type,
1030 self._mandatoryparams,
1031 self._advisoryparams,
1032 self._data,
1033 self.mandatory,
1034 )
985 1035
986 # methods used to defines the part content 1036 # methods used to defines the part content
987 @property 1037 @property
988 def data(self): 1038 def data(self):
989 return self._data 1039 return self._data
1041 if nbap: 1091 if nbap:
1042 msg.append(' %i advisory' % nbmp) 1092 msg.append(' %i advisory' % nbmp)
1043 msg.append(')') 1093 msg.append(')')
1044 if not self.data: 1094 if not self.data:
1045 msg.append(' empty payload') 1095 msg.append(' empty payload')
1046 elif (util.safehasattr(self.data, 'next') 1096 elif util.safehasattr(self.data, 'next') or util.safehasattr(
1047 or util.safehasattr(self.data, '__next__')): 1097 self.data, '__next__'
1098 ):
1048 msg.append(' streamed payload') 1099 msg.append(' streamed payload')
1049 else: 1100 else:
1050 msg.append(' %i bytes payload' % len(self.data)) 1101 msg.append(' %i bytes payload' % len(self.data))
1051 msg.append('\n') 1102 msg.append('\n')
1052 ui.debug(''.join(msg)) 1103 ui.debug(''.join(msg))
1056 parttype = self.type.upper() 1107 parttype = self.type.upper()
1057 else: 1108 else:
1058 parttype = self.type.lower() 1109 parttype = self.type.lower()
1059 outdebug(ui, 'part %s: "%s"' % (pycompat.bytestr(self.id), parttype)) 1110 outdebug(ui, 'part %s: "%s"' % (pycompat.bytestr(self.id), parttype))
1060 ## parttype 1111 ## parttype
1061 header = [_pack(_fparttypesize, len(parttype)), 1112 header = [
1062 parttype, _pack(_fpartid, self.id), 1113 _pack(_fparttypesize, len(parttype)),
1063 ] 1114 parttype,
1115 _pack(_fpartid, self.id),
1116 ]
1064 ## parameters 1117 ## parameters
1065 # count 1118 # count
1066 manpar = self.mandatoryparams 1119 manpar = self.mandatoryparams
1067 advpar = self.advisoryparams 1120 advpar = self.advisoryparams
1068 header.append(_pack(_fpartparamcount, len(manpar), len(advpar))) 1121 header.append(_pack(_fpartparamcount, len(manpar), len(advpar)))
1085 header.append(value) 1138 header.append(value)
1086 ## finalize header 1139 ## finalize header
1087 try: 1140 try:
1088 headerchunk = ''.join(header) 1141 headerchunk = ''.join(header)
1089 except TypeError: 1142 except TypeError:
1090 raise TypeError(r'Found a non-bytes trying to ' 1143 raise TypeError(
1091 r'build bundle part header: %r' % header) 1144 r'Found a non-bytes trying to '
1145 r'build bundle part header: %r' % header
1146 )
1092 outdebug(ui, 'header chunk size: %i' % len(headerchunk)) 1147 outdebug(ui, 'header chunk size: %i' % len(headerchunk))
1093 yield _pack(_fpartheadersize, len(headerchunk)) 1148 yield _pack(_fpartheadersize, len(headerchunk))
1094 yield headerchunk 1149 yield headerchunk
1095 ## payload 1150 ## payload
1096 try: 1151 try:
1105 ui.debug('bundle2-generatorexit\n') 1160 ui.debug('bundle2-generatorexit\n')
1106 raise 1161 raise
1107 except BaseException as exc: 1162 except BaseException as exc:
1108 bexc = stringutil.forcebytestr(exc) 1163 bexc = stringutil.forcebytestr(exc)
1109 # backup exception data for later 1164 # backup exception data for later
1110 ui.debug('bundle2-input-stream-interrupt: encoding exception %s' 1165 ui.debug(
1111 % bexc) 1166 'bundle2-input-stream-interrupt: encoding exception %s' % bexc
1167 )
1112 tb = sys.exc_info()[2] 1168 tb = sys.exc_info()[2]
1113 msg = 'unexpected error: %s' % bexc 1169 msg = 'unexpected error: %s' % bexc
1114 interpart = bundlepart('error:abort', [('message', msg)], 1170 interpart = bundlepart(
1115 mandatory=False) 1171 'error:abort', [('message', msg)], mandatory=False
1172 )
1116 interpart.id = 0 1173 interpart.id = 0
1117 yield _pack(_fpayloadsize, -1) 1174 yield _pack(_fpayloadsize, -1)
1118 for chunk in interpart.getchunks(ui=ui): 1175 for chunk in interpart.getchunks(ui=ui):
1119 yield chunk 1176 yield chunk
1120 outdebug(ui, 'closing payload chunk') 1177 outdebug(ui, 'closing payload chunk')
1130 """yield chunks of a the part payload 1187 """yield chunks of a the part payload
1131 1188
1132 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."""
1133 # we only support fixed size data now. 1190 # we only support fixed size data now.
1134 # This will be improved in the future. 1191 # This will be improved in the future.
1135 if (util.safehasattr(self.data, 'next') 1192 if util.safehasattr(self.data, 'next') or util.safehasattr(
1136 or util.safehasattr(self.data, '__next__')): 1193 self.data, '__next__'
1194 ):
1137 buff = util.chunkbuffer(self.data) 1195 buff = util.chunkbuffer(self.data)
1138 chunk = buff.read(preferedchunksize) 1196 chunk = buff.read(preferedchunksize)
1139 while chunk: 1197 while chunk:
1140 yield chunk 1198 yield chunk
1141 chunk = buff.read(preferedchunksize) 1199 chunk = buff.read(preferedchunksize)
1143 yield self.data 1201 yield self.data
1144 1202
1145 1203
1146 flaginterrupt = -1 1204 flaginterrupt = -1
1147 1205
1206
1148 class interrupthandler(unpackermixin): 1207 class interrupthandler(unpackermixin):
1149 """read one part and process it with restricted capability 1208 """read one part and process it with restricted capability
1150 1209
1151 This allows to transmit exception raised on the producer size during part 1210 This allows to transmit exception raised on the producer size during part
1152 iteration while the consumer is reading a part. 1211 iteration while the consumer is reading a part.
1161 """reads a part header size and return the bytes blob 1220 """reads a part header size and return the bytes blob
1162 1221
1163 returns None if empty""" 1222 returns None if empty"""
1164 headersize = self._unpack(_fpartheadersize)[0] 1223 headersize = self._unpack(_fpartheadersize)[0]
1165 if headersize < 0: 1224 if headersize < 0:
1166 raise error.BundleValueError('negative part header size: %i' 1225 raise error.BundleValueError(
1167 % headersize) 1226 'negative part header size: %i' % headersize
1227 )
1168 indebug(self.ui, 'part header size: %i\n' % headersize) 1228 indebug(self.ui, 'part header size: %i\n' % headersize)
1169 if headersize: 1229 if headersize:
1170 return self._readexact(headersize) 1230 return self._readexact(headersize)
1171 return None 1231 return None
1172 1232
1173 def __call__(self): 1233 def __call__(self):
1174 1234
1175 self.ui.debug('bundle2-input-stream-interrupt:' 1235 self.ui.debug(
1176 ' opening out of band context\n') 1236 'bundle2-input-stream-interrupt:' ' opening out of band context\n'
1237 )
1177 indebug(self.ui, 'bundle2 stream interruption, looking for a part.') 1238 indebug(self.ui, 'bundle2 stream interruption, looking for a part.')
1178 headerblock = self._readpartheader() 1239 headerblock = self._readpartheader()
1179 if headerblock is None: 1240 if headerblock is None:
1180 indebug(self.ui, 'no part found during interruption.') 1241 indebug(self.ui, 'no part found during interruption.')
1181 return 1242 return
1188 hardabort = True 1249 hardabort = True
1189 raise 1250 raise
1190 finally: 1251 finally:
1191 if not hardabort: 1252 if not hardabort:
1192 part.consume() 1253 part.consume()
1193 self.ui.debug('bundle2-input-stream-interrupt:' 1254 self.ui.debug(
1194 ' closing out of band context\n') 1255 'bundle2-input-stream-interrupt:' ' closing out of band context\n'
1256 )
1257
1195 1258
1196 class interruptoperation(object): 1259 class interruptoperation(object):
1197 """A limited operation to be use by part handler during interruption 1260 """A limited operation to be use by part handler during interruption
1198 1261
1199 It only have access to an ui object. 1262 It only have access to an ui object.
1208 def repo(self): 1271 def repo(self):
1209 raise error.ProgrammingError('no repo access from stream interruption') 1272 raise error.ProgrammingError('no repo access from stream interruption')
1210 1273
1211 def gettransaction(self): 1274 def gettransaction(self):
1212 raise TransactionUnavailable('no repo access from stream interruption') 1275 raise TransactionUnavailable('no repo access from stream interruption')
1276
1213 1277
1214 def decodepayloadchunks(ui, fh): 1278 def decodepayloadchunks(ui, fh):
1215 """Reads bundle2 part payload data into chunks. 1279 """Reads bundle2 part payload data into chunks.
1216 1280
1217 Part payload data consists of framed chunks. This function takes 1281 Part payload data consists of framed chunks. This function takes
1233 # changegroup.readexactly() is inlined below for performance. 1297 # changegroup.readexactly() is inlined below for performance.
1234 while chunksize: 1298 while chunksize:
1235 if chunksize >= 0: 1299 if chunksize >= 0:
1236 s = read(chunksize) 1300 s = read(chunksize)
1237 if len(s) < chunksize: 1301 if len(s) < chunksize:
1238 raise error.Abort(_('stream ended unexpectedly ' 1302 raise error.Abort(
1239 ' (got %d bytes, expected %d)') % 1303 _(
1240 (len(s), chunksize)) 1304 'stream ended unexpectedly '
1305 ' (got %d bytes, expected %d)'
1306 )
1307 % (len(s), chunksize)
1308 )
1241 1309
1242 yield s 1310 yield s
1243 elif chunksize == flaginterrupt: 1311 elif chunksize == flaginterrupt:
1244 # Interrupt "signal" detected. The regular stream is interrupted 1312 # Interrupt "signal" detected. The regular stream is interrupted
1245 # and a bundle2 part follows. Consume it. 1313 # and a bundle2 part follows. Consume it.
1246 interrupthandler(ui, fh)() 1314 interrupthandler(ui, fh)()
1247 else: 1315 else:
1248 raise error.BundleValueError( 1316 raise error.BundleValueError(
1249 'negative payload chunk size: %s' % chunksize) 1317 'negative payload chunk size: %s' % chunksize
1318 )
1250 1319
1251 s = read(headersize) 1320 s = read(headersize)
1252 if len(s) < headersize: 1321 if len(s) < headersize:
1253 raise error.Abort(_('stream ended unexpectedly ' 1322 raise error.Abort(
1254 ' (got %d bytes, expected %d)') % 1323 _('stream ended unexpectedly ' ' (got %d bytes, expected %d)')
1255 (len(s), chunksize)) 1324 % (len(s), chunksize)
1325 )
1256 1326
1257 chunksize = unpack(s)[0] 1327 chunksize = unpack(s)[0]
1258 1328
1259 # indebug() inlined for performance. 1329 # indebug() inlined for performance.
1260 if dolog: 1330 if dolog:
1261 debug('bundle2-input: payload chunk size: %i\n' % chunksize) 1331 debug('bundle2-input: payload chunk size: %i\n' % chunksize)
1262 1332
1333
1263 class unbundlepart(unpackermixin): 1334 class unbundlepart(unpackermixin):
1264 """a bundle part read from a bundle""" 1335 """a bundle part read from a bundle"""
1265 1336
1266 def __init__(self, ui, header, fp): 1337 def __init__(self, ui, header, fp):
1267 super(unbundlepart, self).__init__(fp) 1338 super(unbundlepart, self).__init__(fp)
1268 self._seekable = (util.safehasattr(fp, 'seek') and 1339 self._seekable = util.safehasattr(fp, 'seek') and util.safehasattr(
1269 util.safehasattr(fp, 'tell')) 1340 fp, 'tell'
1341 )
1270 self.ui = ui 1342 self.ui = ui
1271 # unbundle state attr 1343 # unbundle state attr
1272 self._headerdata = header 1344 self._headerdata = header
1273 self._headeroffset = 0 1345 self._headeroffset = 0
1274 self._initialized = False 1346 self._initialized = False
1285 self._pos = 0 1357 self._pos = 0
1286 1358
1287 def _fromheader(self, size): 1359 def _fromheader(self, size):
1288 """return the next <size> byte from the header""" 1360 """return the next <size> byte from the header"""
1289 offset = self._headeroffset 1361 offset = self._headeroffset
1290 data = self._headerdata[offset:(offset + size)] 1362 data = self._headerdata[offset : (offset + size)]
1291 self._headeroffset = offset + size 1363 self._headeroffset = offset + size
1292 return data 1364 return data
1293 1365
1294 def _unpackheader(self, format): 1366 def _unpackheader(self, format):
1295 """read given format from header 1367 """read given format from header
1300 1372
1301 def _initparams(self, mandatoryparams, advisoryparams): 1373 def _initparams(self, mandatoryparams, advisoryparams):
1302 """internal function to setup all logic related parameters""" 1374 """internal function to setup all logic related parameters"""
1303 # make it read only to prevent people touching it by mistake. 1375 # make it read only to prevent people touching it by mistake.
1304 self.mandatoryparams = tuple(mandatoryparams) 1376 self.mandatoryparams = tuple(mandatoryparams)
1305 self.advisoryparams = tuple(advisoryparams) 1377 self.advisoryparams = tuple(advisoryparams)
1306 # user friendly UI 1378 # user friendly UI
1307 self.params = util.sortdict(self.mandatoryparams) 1379 self.params = util.sortdict(self.mandatoryparams)
1308 self.params.update(self.advisoryparams) 1380 self.params.update(self.advisoryparams)
1309 self.mandatorykeys = frozenset(p[0] for p in mandatoryparams) 1381 self.mandatorykeys = frozenset(p[0] for p in mandatoryparams)
1310 1382
1314 self.type = self._fromheader(typesize) 1386 self.type = self._fromheader(typesize)
1315 indebug(self.ui, 'part type: "%s"' % self.type) 1387 indebug(self.ui, 'part type: "%s"' % self.type)
1316 self.id = self._unpackheader(_fpartid)[0] 1388 self.id = self._unpackheader(_fpartid)[0]
1317 indebug(self.ui, 'part id: "%s"' % pycompat.bytestr(self.id)) 1389 indebug(self.ui, 'part id: "%s"' % pycompat.bytestr(self.id))
1318 # extract mandatory bit from type 1390 # extract mandatory bit from type
1319 self.mandatory = (self.type != self.type.lower()) 1391 self.mandatory = self.type != self.type.lower()
1320 self.type = self.type.lower() 1392 self.type = self.type.lower()
1321 ## reading parameters 1393 ## reading parameters
1322 # param count 1394 # param count
1323 mancount, advcount = self._unpackheader(_fpartparamcount) 1395 mancount, advcount = self._unpackheader(_fpartparamcount)
1324 indebug(self.ui, 'part parameters: %i' % (mancount + advcount)) 1396 indebug(self.ui, 'part parameters: %i' % (mancount + advcount))
1370 else: 1442 else:
1371 data = self._payloadstream.read(size) 1443 data = self._payloadstream.read(size)
1372 self._pos += len(data) 1444 self._pos += len(data)
1373 if size is None or len(data) < size: 1445 if size is None or len(data) < size:
1374 if not self.consumed and self._pos: 1446 if not self.consumed and self._pos:
1375 self.ui.debug('bundle2-input-part: total payload size %i\n' 1447 self.ui.debug(
1376 % self._pos) 1448 'bundle2-input-part: total payload size %i\n' % self._pos
1449 )
1377 self.consumed = True 1450 self.consumed = True
1378 return data 1451 return data
1452
1379 1453
1380 class seekableunbundlepart(unbundlepart): 1454 class seekableunbundlepart(unbundlepart):
1381 """A bundle2 part in a bundle that is seekable. 1455 """A bundle2 part in a bundle that is seekable.
1382 1456
1383 Regular ``unbundlepart`` instances can only be read once. This class 1457 Regular ``unbundlepart`` instances can only be read once. This class
1392 class maintain a mapping between offsets in the underlying stream and 1466 class maintain a mapping between offsets in the underlying stream and
1393 the decoded payload. This mapping will consume memory in proportion 1467 the decoded payload. This mapping will consume memory in proportion
1394 to the number of chunks within the payload (which almost certainly 1468 to the number of chunks within the payload (which almost certainly
1395 increases in proportion with the size of the part). 1469 increases in proportion with the size of the part).
1396 """ 1470 """
1471
1397 def __init__(self, ui, header, fp): 1472 def __init__(self, ui, header, fp):
1398 # (payload, file) offsets for chunk starts. 1473 # (payload, file) offsets for chunk starts.
1399 self._chunkindex = [] 1474 self._chunkindex = []
1400 1475
1401 super(seekableunbundlepart, self).__init__(ui, header, fp) 1476 super(seekableunbundlepart, self).__init__(ui, header, fp)
1405 if len(self._chunkindex) == 0: 1480 if len(self._chunkindex) == 0:
1406 assert chunknum == 0, 'Must start with chunk 0' 1481 assert chunknum == 0, 'Must start with chunk 0'
1407 self._chunkindex.append((0, self._tellfp())) 1482 self._chunkindex.append((0, self._tellfp()))
1408 else: 1483 else:
1409 assert chunknum < len(self._chunkindex), ( 1484 assert chunknum < len(self._chunkindex), (
1410 'Unknown chunk %d' % chunknum) 1485 'Unknown chunk %d' % chunknum
1486 )
1411 self._seekfp(self._chunkindex[chunknum][1]) 1487 self._seekfp(self._chunkindex[chunknum][1])
1412 1488
1413 pos = self._chunkindex[chunknum][0] 1489 pos = self._chunkindex[chunknum][0]
1414 1490
1415 for chunk in decodepayloadchunks(self.ui, self._fp): 1491 for chunk in decodepayloadchunks(self.ui, self._fp):
1493 self._seekable = False 1569 self._seekable = False
1494 else: 1570 else:
1495 raise 1571 raise
1496 return None 1572 return None
1497 1573
1574
1498 # These are only the static capabilities. 1575 # These are only the static capabilities.
1499 # Check the 'getrepocaps' function for the rest. 1576 # Check the 'getrepocaps' function for the rest.
1500 capabilities = {'HG20': (), 1577 capabilities = {
1501 'bookmarks': (), 1578 'HG20': (),
1502 'error': ('abort', 'unsupportedcontent', 'pushraced', 1579 'bookmarks': (),
1503 'pushkey'), 1580 'error': ('abort', 'unsupportedcontent', 'pushraced', 'pushkey'),
1504 'listkeys': (), 1581 'listkeys': (),
1505 'pushkey': (), 1582 'pushkey': (),
1506 'digests': tuple(sorted(util.DIGESTS.keys())), 1583 'digests': tuple(sorted(util.DIGESTS.keys())),
1507 'remote-changegroup': ('http', 'https'), 1584 'remote-changegroup': ('http', 'https'),
1508 'hgtagsfnodes': (), 1585 'hgtagsfnodes': (),
1509 'rev-branch-cache': (), 1586 'rev-branch-cache': (),
1510 'phases': ('heads',), 1587 'phases': ('heads',),
1511 'stream': ('v2',), 1588 'stream': ('v2',),
1512 } 1589 }
1590
1513 1591
1514 def getrepocaps(repo, allowpushback=False, role=None): 1592 def getrepocaps(repo, allowpushback=False, role=None):
1515 """return the bundle2 capabilities for a given repo 1593 """return the bundle2 capabilities for a given repo
1516 1594
1517 Exists to allow extensions (like evolution) to mutate the capabilities. 1595 Exists to allow extensions (like evolution) to mutate the capabilities.
1522 """ 1600 """
1523 if role not in ('client', 'server'): 1601 if role not in ('client', 'server'):
1524 raise error.ProgrammingError('role argument must be client or server') 1602 raise error.ProgrammingError('role argument must be client or server')
1525 1603
1526 caps = capabilities.copy() 1604 caps = capabilities.copy()
1527 caps['changegroup'] = tuple(sorted( 1605 caps['changegroup'] = tuple(
1528 changegroup.supportedincomingversions(repo))) 1606 sorted(changegroup.supportedincomingversions(repo))
1607 )
1529 if obsolete.isenabled(repo, obsolete.exchangeopt): 1608 if obsolete.isenabled(repo, obsolete.exchangeopt):
1530 supportedformat = tuple('V%i' % v for v in obsolete.formats) 1609 supportedformat = tuple('V%i' % v for v in obsolete.formats)
1531 caps['obsmarkers'] = supportedformat 1610 caps['obsmarkers'] = supportedformat
1532 if allowpushback: 1611 if allowpushback:
1533 caps['pushback'] = () 1612 caps['pushback'] = ()
1537 if 'phases' in repo.ui.configlist('devel', 'legacy.exchange'): 1616 if 'phases' in repo.ui.configlist('devel', 'legacy.exchange'):
1538 caps.pop('phases') 1617 caps.pop('phases')
1539 1618
1540 # 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.
1541 if role == 'server': 1620 if role == 'server':
1542 streamsupported = repo.ui.configbool('server', 'uncompressed', 1621 streamsupported = repo.ui.configbool(
1543 untrusted=True) 1622 'server', 'uncompressed', untrusted=True
1623 )
1544 featuresupported = repo.ui.configbool('server', 'bundle2.stream') 1624 featuresupported = repo.ui.configbool('server', 'bundle2.stream')
1545 1625
1546 if not streamsupported or not featuresupported: 1626 if not streamsupported or not featuresupported:
1547 caps.pop('stream') 1627 caps.pop('stream')
1548 # Else always advertise support on client, because payload support 1628 # Else always advertise support on client, because payload support
1549 # should always be advertised. 1629 # should always be advertised.
1550 1630
1551 return caps 1631 return caps
1632
1552 1633
1553 def bundle2caps(remote): 1634 def bundle2caps(remote):
1554 """return the bundle capabilities of a peer as dict""" 1635 """return the bundle capabilities of a peer as dict"""
1555 raw = remote.capable('bundle2') 1636 raw = remote.capable('bundle2')
1556 if not raw and raw != '': 1637 if not raw and raw != '':
1557 return {} 1638 return {}
1558 capsblob = urlreq.unquote(remote.capable('bundle2')) 1639 capsblob = urlreq.unquote(remote.capable('bundle2'))
1559 return decodecaps(capsblob) 1640 return decodecaps(capsblob)
1560 1641
1642
1561 def obsmarkersversion(caps): 1643 def obsmarkersversion(caps):
1562 """extract the list of supported obsmarkers versions from a bundle2caps dict 1644 """extract the list of supported obsmarkers versions from a bundle2caps dict
1563 """ 1645 """
1564 obscaps = caps.get('obsmarkers', ()) 1646 obscaps = caps.get('obsmarkers', ())
1565 return [int(c[1:]) for c in obscaps if c.startswith('V')] 1647 return [int(c[1:]) for c in obscaps if c.startswith('V')]
1566 1648
1567 def writenewbundle(ui, repo, source, filename, bundletype, outgoing, opts, 1649
1568 vfs=None, compression=None, compopts=None): 1650 def writenewbundle(
1651 ui,
1652 repo,
1653 source,
1654 filename,
1655 bundletype,
1656 outgoing,
1657 opts,
1658 vfs=None,
1659 compression=None,
1660 compopts=None,
1661 ):
1569 if bundletype.startswith('HG10'): 1662 if bundletype.startswith('HG10'):
1570 cg = changegroup.makechangegroup(repo, outgoing, '01', source) 1663 cg = changegroup.makechangegroup(repo, outgoing, '01', source)
1571 return writebundle(ui, cg, filename, bundletype, vfs=vfs, 1664 return writebundle(
1572 compression=compression, compopts=compopts) 1665 ui,
1666 cg,
1667 filename,
1668 bundletype,
1669 vfs=vfs,
1670 compression=compression,
1671 compopts=compopts,
1672 )
1573 elif not bundletype.startswith('HG20'): 1673 elif not bundletype.startswith('HG20'):
1574 raise error.ProgrammingError('unknown bundle type: %s' % bundletype) 1674 raise error.ProgrammingError('unknown bundle type: %s' % bundletype)
1575 1675
1576 caps = {} 1676 caps = {}
1577 if 'obsolescence' in opts: 1677 if 'obsolescence' in opts:
1580 bundle.setcompression(compression, compopts) 1680 bundle.setcompression(compression, compopts)
1581 _addpartsfromopts(ui, repo, bundle, source, outgoing, opts) 1681 _addpartsfromopts(ui, repo, bundle, source, outgoing, opts)
1582 chunkiter = bundle.getchunks() 1682 chunkiter = bundle.getchunks()
1583 1683
1584 return changegroup.writechunks(ui, chunkiter, filename, vfs=vfs) 1684 return changegroup.writechunks(ui, chunkiter, filename, vfs=vfs)
1685
1585 1686
1586 def _addpartsfromopts(ui, repo, bundler, source, outgoing, opts): 1687 def _addpartsfromopts(ui, repo, bundler, source, outgoing, opts):
1587 # We should eventually reconcile this logic with the one behind 1688 # We should eventually reconcile this logic with the one behind
1588 # 'exchange.getbundle2partsgenerator'. 1689 # 'exchange.getbundle2partsgenerator'.
1589 # 1690 #
1599 cgversion = changegroup.safeversion(repo) 1700 cgversion = changegroup.safeversion(repo)
1600 cg = changegroup.makechangegroup(repo, outgoing, cgversion, source) 1701 cg = changegroup.makechangegroup(repo, outgoing, cgversion, source)
1601 part = bundler.newpart('changegroup', data=cg.getchunks()) 1702 part = bundler.newpart('changegroup', data=cg.getchunks())
1602 part.addparam('version', cg.version) 1703 part.addparam('version', cg.version)
1603 if 'clcount' in cg.extras: 1704 if 'clcount' in cg.extras:
1604 part.addparam('nbchanges', '%d' % cg.extras['clcount'], 1705 part.addparam(
1605 mandatory=False) 1706 'nbchanges', '%d' % cg.extras['clcount'], mandatory=False
1606 if opts.get('phases') and repo.revs('%ln and secret()', 1707 )
1607 outgoing.missingheads): 1708 if opts.get('phases') and repo.revs(
1709 '%ln and secret()', outgoing.missingheads
1710 ):
1608 part.addparam('targetphase', '%d' % phases.secret, mandatory=False) 1711 part.addparam('targetphase', '%d' % phases.secret, mandatory=False)
1609 1712
1610 if opts.get('streamv2', False): 1713 if opts.get('streamv2', False):
1611 addpartbundlestream2(bundler, repo, stream=True) 1714 addpartbundlestream2(bundler, repo, stream=True)
1612 1715
1622 1725
1623 if opts.get('phases', False): 1726 if opts.get('phases', False):
1624 headsbyphase = phases.subsetphaseheads(repo, outgoing.missing) 1727 headsbyphase = phases.subsetphaseheads(repo, outgoing.missing)
1625 phasedata = phases.binaryencode(headsbyphase) 1728 phasedata = phases.binaryencode(headsbyphase)
1626 bundler.newpart('phase-heads', data=phasedata) 1729 bundler.newpart('phase-heads', data=phasedata)
1730
1627 1731
1628 def addparttagsfnodescache(repo, bundler, outgoing): 1732 def addparttagsfnodescache(repo, bundler, outgoing):
1629 # we include the tags fnode cache for the bundle changeset 1733 # we include the tags fnode cache for the bundle changeset
1630 # (as an optional parts) 1734 # (as an optional parts)
1631 cache = tags.hgtagsfnodescache(repo.unfiltered()) 1735 cache = tags.hgtagsfnodescache(repo.unfiltered())
1647 chunks.extend([node, fnode]) 1751 chunks.extend([node, fnode])
1648 1752
1649 if chunks: 1753 if chunks:
1650 bundler.newpart('hgtagsfnodes', data=''.join(chunks)) 1754 bundler.newpart('hgtagsfnodes', data=''.join(chunks))
1651 1755
1756
1652 def addpartrevbranchcache(repo, bundler, outgoing): 1757 def addpartrevbranchcache(repo, bundler, outgoing):
1653 # we include the rev branch cache for the bundle changeset 1758 # we include the rev branch cache for the bundle changeset
1654 # (as an optional parts) 1759 # (as an optional parts)
1655 cache = repo.revbranchcache() 1760 cache = repo.revbranchcache()
1656 cl = repo.unfiltered().changelog 1761 cl = repo.unfiltered().changelog
1667 for n in sorted(nodes): 1772 for n in sorted(nodes):
1668 yield n 1773 yield n
1669 for n in sorted(closed): 1774 for n in sorted(closed):
1670 yield n 1775 yield n
1671 1776
1672 bundler.newpart('cache:rev-branch-cache', data=generate(), 1777 bundler.newpart('cache:rev-branch-cache', data=generate(), mandatory=False)
1673 mandatory=False) 1778
1674 1779
1675 def _formatrequirementsspec(requirements): 1780 def _formatrequirementsspec(requirements):
1676 requirements = [req for req in requirements if req != "shared"] 1781 requirements = [req for req in requirements if req != "shared"]
1677 return urlreq.quote(','.join(sorted(requirements))) 1782 return urlreq.quote(','.join(sorted(requirements)))
1783
1678 1784
1679 def _formatrequirementsparams(requirements): 1785 def _formatrequirementsparams(requirements):
1680 requirements = _formatrequirementsspec(requirements) 1786 requirements = _formatrequirementsspec(requirements)
1681 params = "%s%s" % (urlreq.quote("requirements="), requirements) 1787 params = "%s%s" % (urlreq.quote("requirements="), requirements)
1682 return params 1788 return params
1683 1789
1790
1684 def addpartbundlestream2(bundler, repo, **kwargs): 1791 def addpartbundlestream2(bundler, repo, **kwargs):
1685 if not kwargs.get(r'stream', False): 1792 if not kwargs.get(r'stream', False):
1686 return 1793 return
1687 1794
1688 if not streamclone.allowservergeneration(repo): 1795 if not streamclone.allowservergeneration(repo):
1689 raise error.Abort(_('stream data requested but server does not allow ' 1796 raise error.Abort(
1690 'this feature'), 1797 _(
1691 hint=_('well-behaved clients should not be ' 1798 'stream data requested but server does not allow '
1692 'requesting stream data from servers not ' 1799 'this feature'
1693 'advertising it; the client may be buggy')) 1800 ),
1801 hint=_(
1802 'well-behaved clients should not be '
1803 'requesting stream data from servers not '
1804 'advertising it; the client may be buggy'
1805 ),
1806 )
1694 1807
1695 # Stream clones don't compress well. And compression undermines a 1808 # Stream clones don't compress well. And compression undermines a
1696 # goal of stream clones, which is to be fast. Communicate the desire 1809 # goal of stream clones, which is to be fast. Communicate the desire
1697 # to avoid compression to consumers of the bundle. 1810 # to avoid compression to consumers of the bundle.
1698 bundler.prefercompressed = False 1811 bundler.prefercompressed = False
1699 1812
1700 # get the includes and excludes 1813 # get the includes and excludes
1701 includepats = kwargs.get(r'includepats') 1814 includepats = kwargs.get(r'includepats')
1702 excludepats = kwargs.get(r'excludepats') 1815 excludepats = kwargs.get(r'excludepats')
1703 1816
1704 narrowstream = repo.ui.configbool('experimental', 1817 narrowstream = repo.ui.configbool(
1705 'server.stream-narrow-clones') 1818 'experimental', 'server.stream-narrow-clones'
1819 )
1706 1820
1707 if (includepats or excludepats) and not narrowstream: 1821 if (includepats or excludepats) and not narrowstream:
1708 raise error.Abort(_('server does not support narrow stream clones')) 1822 raise error.Abort(_('server does not support narrow stream clones'))
1709 1823
1710 includeobsmarkers = False 1824 includeobsmarkers = False
1711 if repo.obsstore: 1825 if repo.obsstore:
1712 remoteversions = obsmarkersversion(bundler.capabilities) 1826 remoteversions = obsmarkersversion(bundler.capabilities)
1713 if not remoteversions: 1827 if not remoteversions:
1714 raise error.Abort(_('server has obsolescence markers, but client ' 1828 raise error.Abort(
1715 'cannot receive them via stream clone')) 1829 _(
1830 'server has obsolescence markers, but client '
1831 'cannot receive them via stream clone'
1832 )
1833 )
1716 elif repo.obsstore._version in remoteversions: 1834 elif repo.obsstore._version in remoteversions:
1717 includeobsmarkers = True 1835 includeobsmarkers = True
1718 1836
1719 filecount, bytecount, it = streamclone.generatev2(repo, includepats, 1837 filecount, bytecount, it = streamclone.generatev2(
1720 excludepats, 1838 repo, includepats, excludepats, includeobsmarkers
1721 includeobsmarkers) 1839 )
1722 requirements = _formatrequirementsspec(repo.requirements) 1840 requirements = _formatrequirementsspec(repo.requirements)
1723 part = bundler.newpart('stream2', data=it) 1841 part = bundler.newpart('stream2', data=it)
1724 part.addparam('bytecount', '%d' % bytecount, mandatory=True) 1842 part.addparam('bytecount', '%d' % bytecount, mandatory=True)
1725 part.addparam('filecount', '%d' % filecount, mandatory=True) 1843 part.addparam('filecount', '%d' % filecount, mandatory=True)
1726 part.addparam('requirements', requirements, mandatory=True) 1844 part.addparam('requirements', requirements, mandatory=True)
1845
1727 1846
1728 def buildobsmarkerspart(bundler, markers): 1847 def buildobsmarkerspart(bundler, markers):
1729 """add an obsmarker part to the bundler with <markers> 1848 """add an obsmarker part to the bundler with <markers>
1730 1849
1731 No part is created if markers is empty. 1850 No part is created if markers is empty.
1739 if version is None: 1858 if version is None:
1740 raise ValueError('bundler does not support common obsmarker format') 1859 raise ValueError('bundler does not support common obsmarker format')
1741 stream = obsolete.encodemarkers(markers, True, version=version) 1860 stream = obsolete.encodemarkers(markers, True, version=version)
1742 return bundler.newpart('obsmarkers', data=stream) 1861 return bundler.newpart('obsmarkers', data=stream)
1743 1862
1744 def writebundle(ui, cg, filename, bundletype, vfs=None, compression=None, 1863
1745 compopts=None): 1864 def writebundle(
1865 ui, cg, filename, bundletype, vfs=None, compression=None, compopts=None
1866 ):
1746 """Write a bundle file and return its filename. 1867 """Write a bundle file and return its filename.
1747 1868
1748 Existing files will not be overwritten. 1869 Existing files will not be overwritten.
1749 If no filename is specified, a temporary file is created. 1870 If no filename is specified, a temporary file is created.
1750 bz2 compression can be turned off. 1871 bz2 compression can be turned off.
1755 bundle = bundle20(ui) 1876 bundle = bundle20(ui)
1756 bundle.setcompression(compression, compopts) 1877 bundle.setcompression(compression, compopts)
1757 part = bundle.newpart('changegroup', data=cg.getchunks()) 1878 part = bundle.newpart('changegroup', data=cg.getchunks())
1758 part.addparam('version', cg.version) 1879 part.addparam('version', cg.version)
1759 if 'clcount' in cg.extras: 1880 if 'clcount' in cg.extras:
1760 part.addparam('nbchanges', '%d' % cg.extras['clcount'], 1881 part.addparam(
1761 mandatory=False) 1882 'nbchanges', '%d' % cg.extras['clcount'], mandatory=False
1883 )
1762 chunkiter = bundle.getchunks() 1884 chunkiter = bundle.getchunks()
1763 else: 1885 else:
1764 # compression argument is only for the bundle2 case 1886 # compression argument is only for the bundle2 case
1765 assert compression is None 1887 assert compression is None
1766 if cg.version != '01': 1888 if cg.version != '01':
1767 raise error.Abort(_('old bundle types only supports v1 ' 1889 raise error.Abort(
1768 'changegroups')) 1890 _('old bundle types only supports v1 ' 'changegroups')
1891 )
1769 header, comp = bundletypes[bundletype] 1892 header, comp = bundletypes[bundletype]
1770 if comp not in util.compengines.supportedbundletypes: 1893 if comp not in util.compengines.supportedbundletypes:
1771 raise error.Abort(_('unknown stream compression type: %s') 1894 raise error.Abort(_('unknown stream compression type: %s') % comp)
1772 % comp)
1773 compengine = util.compengines.forbundletype(comp) 1895 compengine = util.compengines.forbundletype(comp)
1896
1774 def chunkiter(): 1897 def chunkiter():
1775 yield header 1898 yield header
1776 for chunk in compengine.compressstream(cg.getchunks(), compopts): 1899 for chunk in compengine.compressstream(cg.getchunks(), compopts):
1777 yield chunk 1900 yield chunk
1901
1778 chunkiter = chunkiter() 1902 chunkiter = chunkiter()
1779 1903
1780 # parse the changegroup data, otherwise we will block 1904 # parse the changegroup data, otherwise we will block
1781 # in case of sshrepo because we don't know the end of the stream 1905 # in case of sshrepo because we don't know the end of the stream
1782 return changegroup.writechunks(ui, chunkiter, filename, vfs=vfs) 1906 return changegroup.writechunks(ui, chunkiter, filename, vfs=vfs)
1783 1907
1908
1784 def combinechangegroupresults(op): 1909 def combinechangegroupresults(op):
1785 """logic to combine 0 or more addchangegroup results into one""" 1910 """logic to combine 0 or more addchangegroup results into one"""
1786 results = [r.get('return', 0) 1911 results = [r.get('return', 0) for r in op.records['changegroup']]
1787 for r in op.records['changegroup']]
1788 changedheads = 0 1912 changedheads = 0
1789 result = 1 1913 result = 1
1790 for ret in results: 1914 for ret in results:
1791 # If any changegroup result is 0, return 0 1915 # If any changegroup result is 0, return 0
1792 if ret == 0: 1916 if ret == 0:
1800 result = 1 + changedheads 1924 result = 1 + changedheads
1801 elif changedheads < 0: 1925 elif changedheads < 0:
1802 result = -1 + changedheads 1926 result = -1 + changedheads
1803 return result 1927 return result
1804 1928
1805 @parthandler('changegroup', ('version', 'nbchanges', 'treemanifest', 1929
1806 'targetphase')) 1930 @parthandler(
1931 'changegroup', ('version', 'nbchanges', 'treemanifest', 'targetphase')
1932 )
1807 def handlechangegroup(op, inpart): 1933 def handlechangegroup(op, inpart):
1808 """apply a changegroup part on the repo 1934 """apply a changegroup part on the repo
1809 1935
1810 This is a very early implementation that will massive rework before being 1936 This is a very early implementation that will massive rework before being
1811 inflicted to any end-user. 1937 inflicted to any end-user.
1819 # the source and url passed here are overwritten by the one contained in 1945 # the source and url passed here are overwritten by the one contained in
1820 # the transaction.hookargs argument. So 'bundle2' is a placeholder 1946 # the transaction.hookargs argument. So 'bundle2' is a placeholder
1821 nbchangesets = None 1947 nbchangesets = None
1822 if 'nbchanges' in inpart.params: 1948 if 'nbchanges' in inpart.params:
1823 nbchangesets = int(inpart.params.get('nbchanges')) 1949 nbchangesets = int(inpart.params.get('nbchanges'))
1824 if ('treemanifest' in inpart.params and 1950 if (
1825 'treemanifest' not in op.repo.requirements): 1951 'treemanifest' in inpart.params
1952 and 'treemanifest' not in op.repo.requirements
1953 ):
1826 if len(op.repo.changelog) != 0: 1954 if len(op.repo.changelog) != 0:
1827 raise error.Abort(_( 1955 raise error.Abort(
1828 "bundle contains tree manifests, but local repo is " 1956 _(
1829 "non-empty and does not use tree manifests")) 1957 "bundle contains tree manifests, but local repo is "
1958 "non-empty and does not use tree manifests"
1959 )
1960 )
1830 op.repo.requirements.add('treemanifest') 1961 op.repo.requirements.add('treemanifest')
1831 op.repo.svfs.options = localrepo.resolvestorevfsoptions( 1962 op.repo.svfs.options = localrepo.resolvestorevfsoptions(
1832 op.repo.ui, op.repo.requirements, op.repo.features) 1963 op.repo.ui, op.repo.requirements, op.repo.features
1964 )
1833 op.repo._writerequirements() 1965 op.repo._writerequirements()
1834 extrakwargs = {} 1966 extrakwargs = {}
1835 targetphase = inpart.params.get('targetphase') 1967 targetphase = inpart.params.get('targetphase')
1836 if targetphase is not None: 1968 if targetphase is not None:
1837 extrakwargs[r'targetphase'] = int(targetphase) 1969 extrakwargs[r'targetphase'] = int(targetphase)
1838 ret = _processchangegroup(op, cg, tr, 'bundle2', 'bundle2', 1970 ret = _processchangegroup(
1839 expectedtotal=nbchangesets, **extrakwargs) 1971 op,
1972 cg,
1973 tr,
1974 'bundle2',
1975 'bundle2',
1976 expectedtotal=nbchangesets,
1977 **extrakwargs
1978 )
1840 if op.reply is not None: 1979 if op.reply is not None:
1841 # This is definitely not the final form of this 1980 # This is definitely not the final form of this
1842 # return. But one need to start somewhere. 1981 # return. But one need to start somewhere.
1843 part = op.reply.newpart('reply:changegroup', mandatory=False) 1982 part = op.reply.newpart('reply:changegroup', mandatory=False)
1844 part.addparam( 1983 part.addparam(
1845 'in-reply-to', pycompat.bytestr(inpart.id), mandatory=False) 1984 'in-reply-to', pycompat.bytestr(inpart.id), mandatory=False
1985 )
1846 part.addparam('return', '%i' % ret, mandatory=False) 1986 part.addparam('return', '%i' % ret, mandatory=False)
1847 assert not inpart.read() 1987 assert not inpart.read()
1848 1988
1849 _remotechangegroupparams = tuple(['url', 'size', 'digests'] + 1989
1850 ['digest:%s' % k for k in util.DIGESTS.keys()]) 1990 _remotechangegroupparams = tuple(
1991 ['url', 'size', 'digests'] + ['digest:%s' % k for k in util.DIGESTS.keys()]
1992 )
1993
1994
1851 @parthandler('remote-changegroup', _remotechangegroupparams) 1995 @parthandler('remote-changegroup', _remotechangegroupparams)
1852 def handleremotechangegroup(op, inpart): 1996 def handleremotechangegroup(op, inpart):
1853 """apply a bundle10 on the repo, given an url and validation information 1997 """apply a bundle10 on the repo, given an url and validation information
1854 1998
1855 All the information about the remote bundle to import are given as 1999 All the information about the remote bundle to import are given as
1869 raw_url = inpart.params['url'] 2013 raw_url = inpart.params['url']
1870 except KeyError: 2014 except KeyError:
1871 raise error.Abort(_('remote-changegroup: missing "%s" param') % 'url') 2015 raise error.Abort(_('remote-changegroup: missing "%s" param') % 'url')
1872 parsed_url = util.url(raw_url) 2016 parsed_url = util.url(raw_url)
1873 if parsed_url.scheme not in capabilities['remote-changegroup']: 2017 if parsed_url.scheme not in capabilities['remote-changegroup']:
1874 raise error.Abort(_('remote-changegroup does not support %s urls') % 2018 raise error.Abort(
1875 parsed_url.scheme) 2019 _('remote-changegroup does not support %s urls') % parsed_url.scheme
2020 )
1876 2021
1877 try: 2022 try:
1878 size = int(inpart.params['size']) 2023 size = int(inpart.params['size'])
1879 except ValueError: 2024 except ValueError:
1880 raise error.Abort(_('remote-changegroup: invalid value for param "%s"') 2025 raise error.Abort(
1881 % 'size') 2026 _('remote-changegroup: invalid value for param "%s"') % 'size'
2027 )
1882 except KeyError: 2028 except KeyError:
1883 raise error.Abort(_('remote-changegroup: missing "%s" param') % 'size') 2029 raise error.Abort(_('remote-changegroup: missing "%s" param') % 'size')
1884 2030
1885 digests = {} 2031 digests = {}
1886 for typ in inpart.params.get('digests', '').split(): 2032 for typ in inpart.params.get('digests', '').split():
1887 param = 'digest:%s' % typ 2033 param = 'digest:%s' % typ
1888 try: 2034 try:
1889 value = inpart.params[param] 2035 value = inpart.params[param]
1890 except KeyError: 2036 except KeyError:
1891 raise error.Abort(_('remote-changegroup: missing "%s" param') % 2037 raise error.Abort(
1892 param) 2038 _('remote-changegroup: missing "%s" param') % param
2039 )
1893 digests[typ] = value 2040 digests[typ] = value
1894 2041
1895 real_part = util.digestchecker(url.open(op.ui, raw_url), size, digests) 2042 real_part = util.digestchecker(url.open(op.ui, raw_url), size, digests)
1896 2043
1897 tr = op.gettransaction() 2044 tr = op.gettransaction()
1898 from . import exchange 2045 from . import exchange
2046
1899 cg = exchange.readbundle(op.repo.ui, real_part, raw_url) 2047 cg = exchange.readbundle(op.repo.ui, real_part, raw_url)
1900 if not isinstance(cg, changegroup.cg1unpacker): 2048 if not isinstance(cg, changegroup.cg1unpacker):
1901 raise error.Abort(_('%s: not a bundle version 1.0') % 2049 raise error.Abort(
1902 util.hidepassword(raw_url)) 2050 _('%s: not a bundle version 1.0') % util.hidepassword(raw_url)
2051 )
1903 ret = _processchangegroup(op, cg, tr, 'bundle2', 'bundle2') 2052 ret = _processchangegroup(op, cg, tr, 'bundle2', 'bundle2')
1904 if op.reply is not None: 2053 if op.reply is not None:
1905 # This is definitely not the final form of this 2054 # This is definitely not the final form of this
1906 # return. But one need to start somewhere. 2055 # return. But one need to start somewhere.
1907 part = op.reply.newpart('reply:changegroup') 2056 part = op.reply.newpart('reply:changegroup')
1908 part.addparam( 2057 part.addparam(
1909 'in-reply-to', pycompat.bytestr(inpart.id), mandatory=False) 2058 'in-reply-to', pycompat.bytestr(inpart.id), mandatory=False
2059 )
1910 part.addparam('return', '%i' % ret, mandatory=False) 2060 part.addparam('return', '%i' % ret, mandatory=False)
1911 try: 2061 try:
1912 real_part.validate() 2062 real_part.validate()
1913 except error.Abort as e: 2063 except error.Abort as e:
1914 raise error.Abort(_('bundle at %s is corrupted:\n%s') % 2064 raise error.Abort(
1915 (util.hidepassword(raw_url), bytes(e))) 2065 _('bundle at %s is corrupted:\n%s')
2066 % (util.hidepassword(raw_url), bytes(e))
2067 )
1916 assert not inpart.read() 2068 assert not inpart.read()
2069
1917 2070
1918 @parthandler('reply:changegroup', ('return', 'in-reply-to')) 2071 @parthandler('reply:changegroup', ('return', 'in-reply-to'))
1919 def handlereplychangegroup(op, inpart): 2072 def handlereplychangegroup(op, inpart):
1920 ret = int(inpart.params['return']) 2073 ret = int(inpart.params['return'])
1921 replyto = int(inpart.params['in-reply-to']) 2074 replyto = int(inpart.params['in-reply-to'])
1922 op.records.add('changegroup', {'return': ret}, replyto) 2075 op.records.add('changegroup', {'return': ret}, replyto)
1923 2076
2077
1924 @parthandler('check:bookmarks') 2078 @parthandler('check:bookmarks')
1925 def handlecheckbookmarks(op, inpart): 2079 def handlecheckbookmarks(op, inpart):
1926 """check location of bookmarks 2080 """check location of bookmarks
1927 2081
1928 This part is to be used to detect push race regarding bookmark, it 2082 This part is to be used to detect push race regarding bookmark, it
1929 contains binary encoded (bookmark, node) tuple. If the local state does 2083 contains binary encoded (bookmark, node) tuple. If the local state does
1930 not marks the one in the part, a PushRaced exception is raised 2084 not marks the one in the part, a PushRaced exception is raised
1931 """ 2085 """
1932 bookdata = bookmarks.binarydecode(inpart) 2086 bookdata = bookmarks.binarydecode(inpart)
1933 2087
1934 msgstandard = ('remote repository changed while pushing - please try again ' 2088 msgstandard = (
1935 '(bookmark "%s" move from %s to %s)') 2089 'remote repository changed while pushing - please try again '
1936 msgmissing = ('remote repository changed while pushing - please try again ' 2090 '(bookmark "%s" move from %s to %s)'
1937 '(bookmark "%s" is missing, expected %s)') 2091 )
1938 msgexist = ('remote repository changed while pushing - please try again ' 2092 msgmissing = (
1939 '(bookmark "%s" set on %s, expected missing)') 2093 'remote repository changed while pushing - please try again '
2094 '(bookmark "%s" is missing, expected %s)'
2095 )
2096 msgexist = (
2097 'remote repository changed while pushing - please try again '
2098 '(bookmark "%s" set on %s, expected missing)'
2099 )
1940 for book, node in bookdata: 2100 for book, node in bookdata:
1941 currentnode = op.repo._bookmarks.get(book) 2101 currentnode = op.repo._bookmarks.get(book)
1942 if currentnode != node: 2102 if currentnode != node:
1943 if node is None: 2103 if node is None:
1944 finalmsg = msgexist % (book, nodemod.short(currentnode)) 2104 finalmsg = msgexist % (book, nodemod.short(currentnode))
1945 elif currentnode is None: 2105 elif currentnode is None:
1946 finalmsg = msgmissing % (book, nodemod.short(node)) 2106 finalmsg = msgmissing % (book, nodemod.short(node))
1947 else: 2107 else:
1948 finalmsg = msgstandard % (book, nodemod.short(node), 2108 finalmsg = msgstandard % (
1949 nodemod.short(currentnode)) 2109 book,
2110 nodemod.short(node),
2111 nodemod.short(currentnode),
2112 )
1950 raise error.PushRaced(finalmsg) 2113 raise error.PushRaced(finalmsg)
2114
1951 2115
1952 @parthandler('check:heads') 2116 @parthandler('check:heads')
1953 def handlecheckheads(op, inpart): 2117 def handlecheckheads(op, inpart):
1954 """check that head of the repo did not change 2118 """check that head of the repo did not change
1955 2119
1963 assert not h 2127 assert not h
1964 # Trigger a transaction so that we are guaranteed to have the lock now. 2128 # Trigger a transaction so that we are guaranteed to have the lock now.
1965 if op.ui.configbool('experimental', 'bundle2lazylocking'): 2129 if op.ui.configbool('experimental', 'bundle2lazylocking'):
1966 op.gettransaction() 2130 op.gettransaction()
1967 if sorted(heads) != sorted(op.repo.heads()): 2131 if sorted(heads) != sorted(op.repo.heads()):
1968 raise error.PushRaced('remote repository changed while pushing - ' 2132 raise error.PushRaced(
1969 'please try again') 2133 'remote repository changed while pushing - ' 'please try again'
2134 )
2135
1970 2136
1971 @parthandler('check:updated-heads') 2137 @parthandler('check:updated-heads')
1972 def handlecheckupdatedheads(op, inpart): 2138 def handlecheckupdatedheads(op, inpart):
1973 """check for race on the heads touched by a push 2139 """check for race on the heads touched by a push
1974 2140
1992 for ls in op.repo.branchmap().iterheads(): 2158 for ls in op.repo.branchmap().iterheads():
1993 currentheads.update(ls) 2159 currentheads.update(ls)
1994 2160
1995 for h in heads: 2161 for h in heads:
1996 if h not in currentheads: 2162 if h not in currentheads:
1997 raise error.PushRaced('remote repository changed while pushing - ' 2163 raise error.PushRaced(
1998 'please try again') 2164 'remote repository changed while pushing - ' 'please try again'
2165 )
2166
1999 2167
2000 @parthandler('check:phases') 2168 @parthandler('check:phases')
2001 def handlecheckphases(op, inpart): 2169 def handlecheckphases(op, inpart):
2002 """check that phase boundaries of the repository did not change 2170 """check that phase boundaries of the repository did not change
2003 2171
2005 """ 2173 """
2006 phasetonodes = phases.binarydecode(inpart) 2174 phasetonodes = phases.binarydecode(inpart)
2007 unfi = op.repo.unfiltered() 2175 unfi = op.repo.unfiltered()
2008 cl = unfi.changelog 2176 cl = unfi.changelog
2009 phasecache = unfi._phasecache 2177 phasecache = unfi._phasecache
2010 msg = ('remote repository changed while pushing - please try again ' 2178 msg = (
2011 '(%s is %s expected %s)') 2179 'remote repository changed while pushing - please try again '
2180 '(%s is %s expected %s)'
2181 )
2012 for expectedphase, nodes in enumerate(phasetonodes): 2182 for expectedphase, nodes in enumerate(phasetonodes):
2013 for n in nodes: 2183 for n in nodes:
2014 actualphase = phasecache.phase(unfi, cl.rev(n)) 2184 actualphase = phasecache.phase(unfi, cl.rev(n))
2015 if actualphase != expectedphase: 2185 if actualphase != expectedphase:
2016 finalmsg = msg % (nodemod.short(n), 2186 finalmsg = msg % (
2017 phases.phasenames[actualphase], 2187 nodemod.short(n),
2018 phases.phasenames[expectedphase]) 2188 phases.phasenames[actualphase],
2189 phases.phasenames[expectedphase],
2190 )
2019 raise error.PushRaced(finalmsg) 2191 raise error.PushRaced(finalmsg)
2192
2020 2193
2021 @parthandler('output') 2194 @parthandler('output')
2022 def handleoutput(op, inpart): 2195 def handleoutput(op, inpart):
2023 """forward output captured on the server to the client""" 2196 """forward output captured on the server to the client"""
2024 for line in inpart.read().splitlines(): 2197 for line in inpart.read().splitlines():
2025 op.ui.status(_('remote: %s\n') % line) 2198 op.ui.status(_('remote: %s\n') % line)
2026 2199
2200
2027 @parthandler('replycaps') 2201 @parthandler('replycaps')
2028 def handlereplycaps(op, inpart): 2202 def handlereplycaps(op, inpart):
2029 """Notify that a reply bundle should be created 2203 """Notify that a reply bundle should be created
2030 2204
2031 The payload contains the capabilities information for the reply""" 2205 The payload contains the capabilities information for the reply"""
2032 caps = decodecaps(inpart.read()) 2206 caps = decodecaps(inpart.read())
2033 if op.reply is None: 2207 if op.reply is None:
2034 op.reply = bundle20(op.ui, caps) 2208 op.reply = bundle20(op.ui, caps)
2035 2209
2210
2036 class AbortFromPart(error.Abort): 2211 class AbortFromPart(error.Abort):
2037 """Sub-class of Abort that denotes an error from a bundle2 part.""" 2212 """Sub-class of Abort that denotes an error from a bundle2 part."""
2213
2038 2214
2039 @parthandler('error:abort', ('message', 'hint')) 2215 @parthandler('error:abort', ('message', 'hint'))
2040 def handleerrorabort(op, inpart): 2216 def handleerrorabort(op, inpart):
2041 """Used to transmit abort error over the wire""" 2217 """Used to transmit abort error over the wire"""
2042 raise AbortFromPart(inpart.params['message'], 2218 raise AbortFromPart(
2043 hint=inpart.params.get('hint')) 2219 inpart.params['message'], hint=inpart.params.get('hint')
2044 2220 )
2045 @parthandler('error:pushkey', ('namespace', 'key', 'new', 'old', 'ret', 2221
2046 'in-reply-to')) 2222
2223 @parthandler(
2224 'error:pushkey', ('namespace', 'key', 'new', 'old', 'ret', 'in-reply-to')
2225 )
2047 def handleerrorpushkey(op, inpart): 2226 def handleerrorpushkey(op, inpart):
2048 """Used to transmit failure of a mandatory pushkey over the wire""" 2227 """Used to transmit failure of a mandatory pushkey over the wire"""
2049 kwargs = {} 2228 kwargs = {}
2050 for name in ('namespace', 'key', 'new', 'old', 'ret'): 2229 for name in ('namespace', 'key', 'new', 'old', 'ret'):
2051 value = inpart.params.get(name) 2230 value = inpart.params.get(name)
2052 if value is not None: 2231 if value is not None:
2053 kwargs[name] = value 2232 kwargs[name] = value
2054 raise error.PushkeyFailed(inpart.params['in-reply-to'], 2233 raise error.PushkeyFailed(
2055 **pycompat.strkwargs(kwargs)) 2234 inpart.params['in-reply-to'], **pycompat.strkwargs(kwargs)
2235 )
2236
2056 2237
2057 @parthandler('error:unsupportedcontent', ('parttype', 'params')) 2238 @parthandler('error:unsupportedcontent', ('parttype', 'params'))
2058 def handleerrorunsupportedcontent(op, inpart): 2239 def handleerrorunsupportedcontent(op, inpart):
2059 """Used to transmit unknown content error over the wire""" 2240 """Used to transmit unknown content error over the wire"""
2060 kwargs = {} 2241 kwargs = {}
2065 if params is not None: 2246 if params is not None:
2066 kwargs['params'] = params.split('\0') 2247 kwargs['params'] = params.split('\0')
2067 2248
2068 raise error.BundleUnknownFeatureError(**pycompat.strkwargs(kwargs)) 2249 raise error.BundleUnknownFeatureError(**pycompat.strkwargs(kwargs))
2069 2250
2251
2070 @parthandler('error:pushraced', ('message',)) 2252 @parthandler('error:pushraced', ('message',))
2071 def handleerrorpushraced(op, inpart): 2253 def handleerrorpushraced(op, inpart):
2072 """Used to transmit push race error over the wire""" 2254 """Used to transmit push race error over the wire"""
2073 raise error.ResponseError(_('push failed:'), inpart.params['message']) 2255 raise error.ResponseError(_('push failed:'), inpart.params['message'])
2256
2074 2257
2075 @parthandler('listkeys', ('namespace',)) 2258 @parthandler('listkeys', ('namespace',))
2076 def handlelistkeys(op, inpart): 2259 def handlelistkeys(op, inpart):
2077 """retrieve pushkey namespace content stored in a bundle2""" 2260 """retrieve pushkey namespace content stored in a bundle2"""
2078 namespace = inpart.params['namespace'] 2261 namespace = inpart.params['namespace']
2079 r = pushkey.decodekeys(inpart.read()) 2262 r = pushkey.decodekeys(inpart.read())
2080 op.records.add('listkeys', (namespace, r)) 2263 op.records.add('listkeys', (namespace, r))
2264
2081 2265
2082 @parthandler('pushkey', ('namespace', 'key', 'old', 'new')) 2266 @parthandler('pushkey', ('namespace', 'key', 'old', 'new'))
2083 def handlepushkey(op, inpart): 2267 def handlepushkey(op, inpart):
2084 """process a pushkey request""" 2268 """process a pushkey request"""
2085 dec = pushkey.decode 2269 dec = pushkey.decode
2090 # Grab the transaction to ensure that we have the lock before performing the 2274 # Grab the transaction to ensure that we have the lock before performing the
2091 # pushkey. 2275 # pushkey.
2092 if op.ui.configbool('experimental', 'bundle2lazylocking'): 2276 if op.ui.configbool('experimental', 'bundle2lazylocking'):
2093 op.gettransaction() 2277 op.gettransaction()
2094 ret = op.repo.pushkey(namespace, key, old, new) 2278 ret = op.repo.pushkey(namespace, key, old, new)
2095 record = {'namespace': namespace, 2279 record = {'namespace': namespace, 'key': key, 'old': old, 'new': new}
2096 'key': key,
2097 'old': old,
2098 'new': new}
2099 op.records.add('pushkey', record) 2280 op.records.add('pushkey', record)
2100 if op.reply is not None: 2281 if op.reply is not None:
2101 rpart = op.reply.newpart('reply:pushkey') 2282 rpart = op.reply.newpart('reply:pushkey')
2102 rpart.addparam( 2283 rpart.addparam(
2103 'in-reply-to', pycompat.bytestr(inpart.id), mandatory=False) 2284 'in-reply-to', pycompat.bytestr(inpart.id), mandatory=False
2285 )
2104 rpart.addparam('return', '%i' % ret, mandatory=False) 2286 rpart.addparam('return', '%i' % ret, mandatory=False)
2105 if inpart.mandatory and not ret: 2287 if inpart.mandatory and not ret:
2106 kwargs = {} 2288 kwargs = {}
2107 for key in ('namespace', 'key', 'new', 'old', 'ret'): 2289 for key in ('namespace', 'key', 'new', 'old', 'ret'):
2108 if key in inpart.params: 2290 if key in inpart.params:
2109 kwargs[key] = inpart.params[key] 2291 kwargs[key] = inpart.params[key]
2110 raise error.PushkeyFailed(partid='%d' % inpart.id, 2292 raise error.PushkeyFailed(
2111 **pycompat.strkwargs(kwargs)) 2293 partid='%d' % inpart.id, **pycompat.strkwargs(kwargs)
2294 )
2295
2112 2296
2113 @parthandler('bookmarks') 2297 @parthandler('bookmarks')
2114 def handlebookmark(op, inpart): 2298 def handlebookmark(op, inpart):
2115 """transmit bookmark information 2299 """transmit bookmark information
2116 2300
2145 hookargs['old'] = nodemod.hex(bookstore.get(book, '')) 2329 hookargs['old'] = nodemod.hex(bookstore.get(book, ''))
2146 hookargs['new'] = nodemod.hex(node if node is not None else '') 2330 hookargs['new'] = nodemod.hex(node if node is not None else '')
2147 allhooks.append(hookargs) 2331 allhooks.append(hookargs)
2148 2332
2149 for hookargs in allhooks: 2333 for hookargs in allhooks:
2150 op.repo.hook('prepushkey', throw=True, 2334 op.repo.hook(
2151 **pycompat.strkwargs(hookargs)) 2335 'prepushkey', throw=True, **pycompat.strkwargs(hookargs)
2336 )
2152 2337
2153 bookstore.applychanges(op.repo, op.gettransaction(), changes) 2338 bookstore.applychanges(op.repo, op.gettransaction(), changes)
2154 2339
2155 if pushkeycompat: 2340 if pushkeycompat:
2341
2156 def runhook(): 2342 def runhook():
2157 for hookargs in allhooks: 2343 for hookargs in allhooks:
2158 op.repo.hook('pushkey', **pycompat.strkwargs(hookargs)) 2344 op.repo.hook('pushkey', **pycompat.strkwargs(hookargs))
2345
2159 op.repo._afterlock(runhook) 2346 op.repo._afterlock(runhook)
2160 2347
2161 elif bookmarksmode == 'records': 2348 elif bookmarksmode == 'records':
2162 for book, node in changes: 2349 for book, node in changes:
2163 record = {'bookmark': book, 'node': node} 2350 record = {'bookmark': book, 'node': node}
2164 op.records.add('bookmarks', record) 2351 op.records.add('bookmarks', record)
2165 else: 2352 else:
2166 raise error.ProgrammingError('unkown bookmark mode: %s' % bookmarksmode) 2353 raise error.ProgrammingError('unkown bookmark mode: %s' % bookmarksmode)
2167 2354
2355
2168 @parthandler('phase-heads') 2356 @parthandler('phase-heads')
2169 def handlephases(op, inpart): 2357 def handlephases(op, inpart):
2170 """apply phases from bundle part to repo""" 2358 """apply phases from bundle part to repo"""
2171 headsbyphase = phases.binarydecode(inpart) 2359 headsbyphase = phases.binarydecode(inpart)
2172 phases.updatephases(op.repo.unfiltered(), op.gettransaction, headsbyphase) 2360 phases.updatephases(op.repo.unfiltered(), op.gettransaction, headsbyphase)
2361
2173 2362
2174 @parthandler('reply:pushkey', ('return', 'in-reply-to')) 2363 @parthandler('reply:pushkey', ('return', 'in-reply-to'))
2175 def handlepushkeyreply(op, inpart): 2364 def handlepushkeyreply(op, inpart):
2176 """retrieve the result of a pushkey request""" 2365 """retrieve the result of a pushkey request"""
2177 ret = int(inpart.params['return']) 2366 ret = int(inpart.params['return'])
2178 partid = int(inpart.params['in-reply-to']) 2367 partid = int(inpart.params['in-reply-to'])
2179 op.records.add('pushkey', {'return': ret}, partid) 2368 op.records.add('pushkey', {'return': ret}, partid)
2180 2369
2370
2181 @parthandler('obsmarkers') 2371 @parthandler('obsmarkers')
2182 def handleobsmarker(op, inpart): 2372 def handleobsmarker(op, inpart):
2183 """add a stream of obsmarkers to the repo""" 2373 """add a stream of obsmarkers to the repo"""
2184 tr = op.gettransaction() 2374 tr = op.gettransaction()
2185 markerdata = inpart.read() 2375 markerdata = inpart.read()
2186 if op.ui.config('experimental', 'obsmarkers-exchange-debug'): 2376 if op.ui.config('experimental', 'obsmarkers-exchange-debug'):
2187 op.ui.write(('obsmarker-exchange: %i bytes received\n') 2377 op.ui.write('obsmarker-exchange: %i bytes received\n' % len(markerdata))
2188 % len(markerdata))
2189 # The mergemarkers call will crash if marker creation is not enabled. 2378 # The mergemarkers call will crash if marker creation is not enabled.
2190 # we want to avoid this if the part is advisory. 2379 # we want to avoid this if the part is advisory.
2191 if not inpart.mandatory and op.repo.obsstore.readonly: 2380 if not inpart.mandatory and op.repo.obsstore.readonly:
2192 op.repo.ui.debug('ignoring obsolescence markers, feature not enabled\n') 2381 op.repo.ui.debug('ignoring obsolescence markers, feature not enabled\n')
2193 return 2382 return
2195 op.repo.invalidatevolatilesets() 2384 op.repo.invalidatevolatilesets()
2196 op.records.add('obsmarkers', {'new': new}) 2385 op.records.add('obsmarkers', {'new': new})
2197 if op.reply is not None: 2386 if op.reply is not None:
2198 rpart = op.reply.newpart('reply:obsmarkers') 2387 rpart = op.reply.newpart('reply:obsmarkers')
2199 rpart.addparam( 2388 rpart.addparam(
2200 'in-reply-to', pycompat.bytestr(inpart.id), mandatory=False) 2389 'in-reply-to', pycompat.bytestr(inpart.id), mandatory=False
2390 )
2201 rpart.addparam('new', '%i' % new, mandatory=False) 2391 rpart.addparam('new', '%i' % new, mandatory=False)
2202 2392
2203 2393
2204 @parthandler('reply:obsmarkers', ('new', 'in-reply-to')) 2394 @parthandler('reply:obsmarkers', ('new', 'in-reply-to'))
2205 def handleobsmarkerreply(op, inpart): 2395 def handleobsmarkerreply(op, inpart):
2206 """retrieve the result of a pushkey request""" 2396 """retrieve the result of a pushkey request"""
2207 ret = int(inpart.params['new']) 2397 ret = int(inpart.params['new'])
2208 partid = int(inpart.params['in-reply-to']) 2398 partid = int(inpart.params['in-reply-to'])
2209 op.records.add('obsmarkers', {'new': ret}, partid) 2399 op.records.add('obsmarkers', {'new': ret}, partid)
2400
2210 2401
2211 @parthandler('hgtagsfnodes') 2402 @parthandler('hgtagsfnodes')
2212 def handlehgtagsfnodes(op, inpart): 2403 def handlehgtagsfnodes(op, inpart):
2213 """Applies .hgtags fnodes cache entries to the local repo. 2404 """Applies .hgtags fnodes cache entries to the local repo.
2214 2405
2230 count += 1 2421 count += 1
2231 2422
2232 cache.write() 2423 cache.write()
2233 op.ui.debug('applied %i hgtags fnodes cache entries\n' % count) 2424 op.ui.debug('applied %i hgtags fnodes cache entries\n' % count)
2234 2425
2426
2235 rbcstruct = struct.Struct('>III') 2427 rbcstruct = struct.Struct('>III')
2428
2236 2429
2237 @parthandler('cache:rev-branch-cache') 2430 @parthandler('cache:rev-branch-cache')
2238 def handlerbc(op, inpart): 2431 def handlerbc(op, inpart):
2239 """receive a rev-branch-cache payload and update the local cache 2432 """receive a rev-branch-cache payload and update the local cache
2240 2433
2264 rev = cl.rev(node) 2457 rev = cl.rev(node)
2265 cache.setdata(branch, rev, node, True) 2458 cache.setdata(branch, rev, node, True)
2266 rawheader = inpart.read(rbcstruct.size) 2459 rawheader = inpart.read(rbcstruct.size)
2267 cache.write() 2460 cache.write()
2268 2461
2462
2269 @parthandler('pushvars') 2463 @parthandler('pushvars')
2270 def bundle2getvars(op, part): 2464 def bundle2getvars(op, part):
2271 '''unbundle a bundle2 containing shellvars on the server''' 2465 '''unbundle a bundle2 containing shellvars on the server'''
2272 # An option to disable unbundling on server-side for security reasons 2466 # An option to disable unbundling on server-side for security reasons
2273 if op.ui.configbool('push', 'pushvars.server'): 2467 if op.ui.configbool('push', 'pushvars.server'):
2278 # they came from the --pushvar flag. 2472 # they came from the --pushvar flag.
2279 key = "USERVAR_" + key 2473 key = "USERVAR_" + key
2280 hookargs[key] = value 2474 hookargs[key] = value
2281 op.addhookargs(hookargs) 2475 op.addhookargs(hookargs)
2282 2476
2477
2283 @parthandler('stream2', ('requirements', 'filecount', 'bytecount')) 2478 @parthandler('stream2', ('requirements', 'filecount', 'bytecount'))
2284 def handlestreamv2bundle(op, part): 2479 def handlestreamv2bundle(op, part):
2285 2480
2286 requirements = urlreq.unquote(part.params['requirements']).split(',') 2481 requirements = urlreq.unquote(part.params['requirements']).split(',')
2287 filecount = int(part.params['filecount']) 2482 filecount = int(part.params['filecount'])
2291 if len(repo): 2486 if len(repo):
2292 msg = _('cannot apply stream clone to non empty repository') 2487 msg = _('cannot apply stream clone to non empty repository')
2293 raise error.Abort(msg) 2488 raise error.Abort(msg)
2294 2489
2295 repo.ui.debug('applying stream bundle\n') 2490 repo.ui.debug('applying stream bundle\n')
2296 streamclone.applybundlev2(repo, part, filecount, bytecount, 2491 streamclone.applybundlev2(repo, part, filecount, bytecount, requirements)
2297 requirements) 2492
2298 2493
2299 def widen_bundle(bundler, repo, oldmatcher, newmatcher, common, 2494 def widen_bundle(
2300 known, cgversion, ellipses): 2495 bundler, repo, oldmatcher, newmatcher, common, known, cgversion, ellipses
2496 ):
2301 """generates bundle2 for widening a narrow clone 2497 """generates bundle2 for widening a narrow clone
2302 2498
2303 bundler is the bundle to which data should be added 2499 bundler is the bundle to which data should be added
2304 repo is the localrepository instance 2500 repo is the localrepository instance
2305 oldmatcher matches what the client already has 2501 oldmatcher matches what the client already has
2316 for r in repo.revs("::%ln", common): 2512 for r in repo.revs("::%ln", common):
2317 commonnodes.add(cl.node(r)) 2513 commonnodes.add(cl.node(r))
2318 if commonnodes: 2514 if commonnodes:
2319 # XXX: we should only send the filelogs (and treemanifest). user 2515 # XXX: we should only send the filelogs (and treemanifest). user
2320 # already has the changelog and manifest 2516 # already has the changelog and manifest
2321 packer = changegroup.getbundler(cgversion, repo, 2517 packer = changegroup.getbundler(
2322 oldmatcher=oldmatcher, 2518 cgversion,
2323 matcher=newmatcher, 2519 repo,
2324 fullnodes=commonnodes) 2520 oldmatcher=oldmatcher,
2325 cgdata = packer.generate({nodemod.nullid}, list(commonnodes), 2521 matcher=newmatcher,
2326 False, 'narrow_widen', changelog=False) 2522 fullnodes=commonnodes,
2523 )
2524 cgdata = packer.generate(
2525 {nodemod.nullid},
2526 list(commonnodes),
2527 False,
2528 'narrow_widen',
2529 changelog=False,
2530 )
2327 2531
2328 part = bundler.newpart('changegroup', data=cgdata) 2532 part = bundler.newpart('changegroup', data=cgdata)
2329 part.addparam('version', cgversion) 2533 part.addparam('version', cgversion)
2330 if 'treemanifest' in repo.requirements: 2534 if 'treemanifest' in repo.requirements:
2331 part.addparam('treemanifest', '1') 2535 part.addparam('treemanifest', '1')