Mercurial > hg
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, |