comparison mercurial/changegroup.py @ 39016:39b8277e2115

changegroup: differentiate between fulltext and diff based deltas Previously, revisiondelta encoded a delta and an optional prefix containing a delta header. The underlying code could populate the delta with either a real delta or a fulltext revision. Following the theme of wanting to defer serialization of revision data to the changegroup format as long as possible, it seems prudent for the revision delta instance to capture what type of data is being represented. This could possibly allow us to encode revision data differently in the future. But for the short term, it makes the behavior of a revisiondelta more explicit. Differential Revision: https://phab.mercurial-scm.org/D4213
author Gregory Szorc <gregory.szorc@gmail.com>
date Wed, 08 Aug 2018 16:01:26 -0700
parents ad9eccedb379
children ef3d3a2f9aa5
comparison
equal deleted inserted replaced
39015:ad9eccedb379 39016:39b8277e2115
505 class revisiondelta(object): 505 class revisiondelta(object):
506 """Describes a delta entry in a changegroup. 506 """Describes a delta entry in a changegroup.
507 507
508 Captured data is sufficient to serialize the delta into multiple 508 Captured data is sufficient to serialize the delta into multiple
509 formats. 509 formats.
510
511 ``revision`` and ``delta`` are mutually exclusive.
510 """ 512 """
511 # 20 byte node of this revision. 513 # 20 byte node of this revision.
512 node = attr.ib() 514 node = attr.ib()
513 # 20 byte nodes of parent revisions. 515 # 20 byte nodes of parent revisions.
514 p1node = attr.ib() 516 p1node = attr.ib()
517 basenode = attr.ib() 519 basenode = attr.ib()
518 # 20 byte node of changeset revision this delta is associated with. 520 # 20 byte node of changeset revision this delta is associated with.
519 linknode = attr.ib() 521 linknode = attr.ib()
520 # 2 bytes of flags to apply to revision data. 522 # 2 bytes of flags to apply to revision data.
521 flags = attr.ib() 523 flags = attr.ib()
522 # Iterable of chunks holding raw delta data. 524 # Size of base revision this delta is against. May be None if
523 deltachunks = attr.ib() 525 # basenode is nullid.
526 baserevisionsize = attr.ib()
527 # Raw fulltext revision data.
528 revision = attr.ib()
529 # Delta between the basenode and node.
530 delta = attr.ib()
524 531
525 def _revisiondeltatochunks(delta, headerfn): 532 def _revisiondeltatochunks(delta, headerfn):
526 """Serialize a revisiondelta to changegroup chunks.""" 533 """Serialize a revisiondelta to changegroup chunks."""
534
535 # The captured revision delta may be encoded as a delta against
536 # a base revision or as a full revision. The changegroup format
537 # requires that everything on the wire be deltas. So for full
538 # revisions, we need to invent a header that says to rewrite
539 # data.
540
541 if delta.delta is not None:
542 prefix, data = b'', delta.delta
543 elif delta.basenode == nullid:
544 data = delta.revision
545 prefix = mdiff.trivialdiffheader(len(data))
546 else:
547 data = delta.revision
548 prefix = mdiff.replacediffheader(delta.baserevisionsize,
549 len(data))
550
527 meta = headerfn(delta) 551 meta = headerfn(delta)
528 l = len(meta) + sum(len(x) for x in delta.deltachunks) 552
529 yield chunkheader(l) 553 yield chunkheader(len(meta) + len(prefix) + len(data))
530 yield meta 554 yield meta
531 for x in delta.deltachunks: 555 if prefix:
532 yield x 556 yield prefix
557 yield data
533 558
534 def _sortnodesnormal(store, nodes, reorder): 559 def _sortnodesnormal(store, nodes, reorder):
535 """Sort nodes for changegroup generation and turn into revnums.""" 560 """Sort nodes for changegroup generation and turn into revnums."""
536 # for generaldelta revlogs, we linearize the revs; this will both be 561 # for generaldelta revlogs, we linearize the revs; this will both be
537 # much quicker and generate a much smaller bundle 562 # much quicker and generate a much smaller bundle
566 """Construct a revision delta for non-ellipses changegroup generation.""" 591 """Construct a revision delta for non-ellipses changegroup generation."""
567 node = store.node(rev) 592 node = store.node(rev)
568 p1, p2 = store.parentrevs(rev) 593 p1, p2 = store.parentrevs(rev)
569 base = deltaparentfn(store, rev, p1, p2, prev) 594 base = deltaparentfn(store, rev, p1, p2, prev)
570 595
571 prefix = '' 596 revision = None
597 delta = None
598 baserevisionsize = None
599
572 if store.iscensored(base) or store.iscensored(rev): 600 if store.iscensored(base) or store.iscensored(rev):
573 try: 601 try:
574 delta = store.revision(node, raw=True) 602 revision = store.revision(node, raw=True)
575 except error.CensoredNodeError as e: 603 except error.CensoredNodeError as e:
576 delta = e.tombstone 604 revision = e.tombstone
577 if base == nullrev: 605
578 prefix = mdiff.trivialdiffheader(len(delta)) 606 if base != nullrev:
579 else: 607 baserevisionsize = store.rawsize(base)
580 baselen = store.rawsize(base) 608
581 prefix = mdiff.replacediffheader(baselen, len(delta))
582 elif base == nullrev: 609 elif base == nullrev:
583 delta = store.revision(node, raw=True) 610 revision = store.revision(node, raw=True)
584 prefix = mdiff.trivialdiffheader(len(delta))
585 else: 611 else:
586 delta = store.revdiff(base, rev) 612 delta = store.revdiff(base, rev)
613
587 p1n, p2n = store.parents(node) 614 p1n, p2n = store.parents(node)
588 615
589 return revisiondelta( 616 return revisiondelta(
590 node=node, 617 node=node,
591 p1node=p1n, 618 p1node=p1n,
592 p2node=p2n, 619 p2node=p2n,
593 basenode=store.node(base), 620 basenode=store.node(base),
594 linknode=linknode, 621 linknode=linknode,
595 flags=store.flags(rev), 622 flags=store.flags(rev),
596 deltachunks=(prefix, delta), 623 baserevisionsize=baserevisionsize,
624 revision=revision,
625 delta=delta,
597 ) 626 )
598 627
599 def _revisiondeltanarrow(cl, store, ischangelog, rev, linkrev, 628 def _revisiondeltanarrow(cl, store, ischangelog, rev, linkrev,
600 linknode, clrevtolocalrev, fullclnodes, 629 linknode, clrevtolocalrev, fullclnodes,
601 precomputedellipsis): 630 precomputedellipsis):
675 p1n, p2n = store.node(p1), store.node(p2) 704 p1n, p2n = store.node(p1), store.node(p2)
676 flags = store.flags(rev) 705 flags = store.flags(rev)
677 flags |= revlog.REVIDX_ELLIPSIS 706 flags |= revlog.REVIDX_ELLIPSIS
678 707
679 # TODO: try and actually send deltas for ellipsis data blocks 708 # TODO: try and actually send deltas for ellipsis data blocks
680 data = store.revision(n)
681 diffheader = mdiff.trivialdiffheader(len(data))
682 709
683 return revisiondelta( 710 return revisiondelta(
684 node=n, 711 node=n,
685 p1node=p1n, 712 p1node=p1n,
686 p2node=p2n, 713 p2node=p2n,
687 basenode=nullid, 714 basenode=nullid,
688 linknode=linknode, 715 linknode=linknode,
689 flags=flags, 716 flags=flags,
690 deltachunks=(diffheader, data), 717 baserevisionsize=None,
718 revision=store.revision(n),
719 delta=None,
691 ) 720 )
692 721
693 def deltagroup(repo, revs, store, ischangelog, lookup, deltaparentfn, 722 def deltagroup(repo, revs, store, ischangelog, lookup, deltaparentfn,
694 units=None, 723 units=None,
695 ellipses=False, clrevtolocalrev=None, fullclnodes=None, 724 ellipses=False, clrevtolocalrev=None, fullclnodes=None,