Show More
@@ -31,7 +31,6 b' from . import (' | |||
|
31 | 31 | phases, |
|
32 | 32 | pycompat, |
|
33 | 33 | repository, |
|
34 | revlog, | |
|
35 | 34 | util, |
|
36 | 35 | ) |
|
37 | 36 | |
@@ -512,19 +511,6 b' class revisiondeltarequest(object):' | |||
|
512 | 511 | basenode = attr.ib() |
|
513 | 512 | ellipsis = attr.ib(default=False) |
|
514 | 513 | |
|
515 | @interfaceutil.implementer(repository.irevisiondelta) | |
|
516 | @attr.s(slots=True, frozen=True) | |
|
517 | class revisiondelta(object): | |
|
518 | node = attr.ib() | |
|
519 | p1node = attr.ib() | |
|
520 | p2node = attr.ib() | |
|
521 | basenode = attr.ib() | |
|
522 | linknode = attr.ib() | |
|
523 | flags = attr.ib() | |
|
524 | baserevisionsize = attr.ib() | |
|
525 | revision = attr.ib() | |
|
526 | delta = attr.ib() | |
|
527 | ||
|
528 | 514 | def _revisiondeltatochunks(delta, headerfn): |
|
529 | 515 | """Serialize a revisiondelta to changegroup chunks.""" |
|
530 | 516 | |
@@ -583,77 +569,6 b' def _sortnodesellipsis(store, nodes, cl,' | |||
|
583 | 569 | key = lambda n: cl.rev(lookup(n)) |
|
584 | 570 | return [store.rev(n) for n in sorted(nodes, key=key)] |
|
585 | 571 | |
|
586 | def _handlerevisiondeltarequest(store, request, prevnode): | |
|
587 | """Obtain a revisiondelta from a revisiondeltarequest""" | |
|
588 | ||
|
589 | node = request.node | |
|
590 | rev = store.rev(node) | |
|
591 | ||
|
592 | # Requesting a full revision. | |
|
593 | if request.basenode == nullid: | |
|
594 | baserev = nullrev | |
|
595 | # Requesting an explicit revision. | |
|
596 | elif request.basenode is not None: | |
|
597 | baserev = store.rev(request.basenode) | |
|
598 | # Allowing us to choose. | |
|
599 | else: | |
|
600 | p1, p2 = store.parentrevs(rev) | |
|
601 | dp = store.deltaparent(rev) | |
|
602 | ||
|
603 | if dp == nullrev and store.storedeltachains: | |
|
604 | # Avoid sending full revisions when delta parent is null. Pick prev | |
|
605 | # in that case. It's tempting to pick p1 in this case, as p1 will | |
|
606 | # be smaller in the common case. However, computing a delta against | |
|
607 | # p1 may require resolving the raw text of p1, which could be | |
|
608 | # expensive. The revlog caches should have prev cached, meaning | |
|
609 | # less CPU for changegroup generation. There is likely room to add | |
|
610 | # a flag and/or config option to control this behavior. | |
|
611 | baserev = store.rev(prevnode) | |
|
612 | elif dp == nullrev: | |
|
613 | # revlog is configured to use full snapshot for a reason, | |
|
614 | # stick to full snapshot. | |
|
615 | baserev = nullrev | |
|
616 | elif dp not in (p1, p2, store.rev(prevnode)): | |
|
617 | # Pick prev when we can't be sure remote has the base revision. | |
|
618 | baserev = store.rev(prevnode) | |
|
619 | else: | |
|
620 | baserev = dp | |
|
621 | ||
|
622 | if baserev != nullrev and not store.candelta(baserev, rev): | |
|
623 | baserev = nullrev | |
|
624 | ||
|
625 | revision = None | |
|
626 | delta = None | |
|
627 | baserevisionsize = None | |
|
628 | ||
|
629 | if store.iscensored(baserev) or store.iscensored(rev): | |
|
630 | try: | |
|
631 | revision = store.revision(node, raw=True) | |
|
632 | except error.CensoredNodeError as e: | |
|
633 | revision = e.tombstone | |
|
634 | ||
|
635 | if baserev != nullrev: | |
|
636 | baserevisionsize = store.rawsize(baserev) | |
|
637 | ||
|
638 | elif baserev == nullrev: | |
|
639 | revision = store.revision(node, raw=True) | |
|
640 | else: | |
|
641 | delta = store.revdiff(baserev, rev) | |
|
642 | ||
|
643 | extraflags = revlog.REVIDX_ELLIPSIS if request.ellipsis else 0 | |
|
644 | ||
|
645 | return revisiondelta( | |
|
646 | node=node, | |
|
647 | p1node=request.p1node, | |
|
648 | p2node=request.p2node, | |
|
649 | linknode=request.linknode, | |
|
650 | basenode=store.node(baserev), | |
|
651 | flags=store.flags(rev) | extraflags, | |
|
652 | baserevisionsize=baserevisionsize, | |
|
653 | revision=revision, | |
|
654 | delta=delta, | |
|
655 | ) | |
|
656 | ||
|
657 | 572 | def _makenarrowdeltarequest(cl, store, ischangelog, rev, node, linkrev, |
|
658 | 573 | linknode, clrevtolocalrev, fullclnodes, |
|
659 | 574 | precomputedellipsis): |
@@ -832,17 +747,12 b' def deltagroup(repo, store, nodes, ischa' | |||
|
832 | 747 | progress = repo.ui.makeprogress(_('bundling'), unit=units, |
|
833 | 748 | total=len(requests)) |
|
834 | 749 | |
|
835 | prevnode = store.node(revs[0]) | |
|
836 | for i, request in enumerate(requests): | |
|
750 | for i, delta in enumerate(store.emitrevisiondeltas(requests)): | |
|
837 | 751 | if progress: |
|
838 | 752 | progress.update(i + 1) |
|
839 | 753 | |
|
840 | delta = _handlerevisiondeltarequest(store, request, prevnode) | |
|
841 | ||
|
842 | 754 | yield delta |
|
843 | 755 | |
|
844 | prevnode = request.node | |
|
845 | ||
|
846 | 756 | if progress: |
|
847 | 757 | progress.complete() |
|
848 | 758 |
@@ -95,6 +95,9 b' class filelog(object):' | |||
|
95 | 95 | def revdiff(self, rev1, rev2): |
|
96 | 96 | return self._revlog.revdiff(rev1, rev2) |
|
97 | 97 | |
|
98 | def emitrevisiondeltas(self, requests): | |
|
99 | return self._revlog.emitrevisiondeltas(requests) | |
|
100 | ||
|
98 | 101 | def addrevision(self, revisiondata, transaction, linkrev, p1, p2, |
|
99 | 102 | node=None, flags=revlog.REVIDX_DEFAULT_FLAGS, |
|
100 | 103 | cachedelta=None): |
@@ -621,6 +621,30 b' class ifiledata(interfaceutil.Interface)' | |||
|
621 | 621 | revision data. |
|
622 | 622 | """ |
|
623 | 623 | |
|
624 | def emitrevisiondeltas(requests): | |
|
625 | """Produce ``irevisiondelta`` from ``irevisiondeltarequest``s. | |
|
626 | ||
|
627 | Given an iterable of objects conforming to the ``irevisiondeltarequest`` | |
|
628 | interface, emits objects conforming to the ``irevisiondelta`` | |
|
629 | interface. | |
|
630 | ||
|
631 | This method is a generator. | |
|
632 | ||
|
633 | ``irevisiondelta`` should be emitted in the same order of | |
|
634 | ``irevisiondeltarequest`` that was passed in. | |
|
635 | ||
|
636 | The emitted objects MUST conform by the results of | |
|
637 | ``irevisiondeltarequest``. Namely, they must respect any requests | |
|
638 | for building a delta from a specific ``basenode`` if defined. | |
|
639 | ||
|
640 | When sending deltas, implementations must take into account whether | |
|
641 | the client has the base delta before encoding a delta against that | |
|
642 | revision. A revision encountered previously in ``requests`` is | |
|
643 | always a suitable base revision. An example of a bad delta is a delta | |
|
644 | against a non-ancestor revision. Another example of a bad delta is a | |
|
645 | delta against a censored revision. | |
|
646 | """ | |
|
647 | ||
|
624 | 648 | class ifilemutation(interfaceutil.Interface): |
|
625 | 649 | """Storage interface for mutation events of a tracked file.""" |
|
626 | 650 |
@@ -45,10 +45,12 b' from . import (' | |||
|
45 | 45 | mdiff, |
|
46 | 46 | policy, |
|
47 | 47 | pycompat, |
|
48 | repository, | |
|
48 | 49 | templatefilters, |
|
49 | 50 | util, |
|
50 | 51 | ) |
|
51 | 52 | from .utils import ( |
|
53 | interfaceutil, | |
|
52 | 54 | stringutil, |
|
53 | 55 | ) |
|
54 | 56 | |
@@ -821,6 +823,19 b' class _revisioninfo(object):' | |||
|
821 | 823 | cachedelta = attr.ib() |
|
822 | 824 | flags = attr.ib() |
|
823 | 825 | |
|
826 | @interfaceutil.implementer(repository.irevisiondelta) | |
|
827 | @attr.s(slots=True, frozen=True) | |
|
828 | class revlogrevisiondelta(object): | |
|
829 | node = attr.ib() | |
|
830 | p1node = attr.ib() | |
|
831 | p2node = attr.ib() | |
|
832 | basenode = attr.ib() | |
|
833 | linknode = attr.ib() | |
|
834 | flags = attr.ib() | |
|
835 | baserevisionsize = attr.ib() | |
|
836 | revision = attr.ib() | |
|
837 | delta = attr.ib() | |
|
838 | ||
|
824 | 839 | # index v0: |
|
825 | 840 | # 4 bytes: offset |
|
826 | 841 | # 4 bytes: compressed length |
@@ -2950,6 +2965,87 b' class revlog(object):' | |||
|
2950 | 2965 | res.append(self.datafile) |
|
2951 | 2966 | return res |
|
2952 | 2967 | |
|
2968 | def emitrevisiondeltas(self, requests): | |
|
2969 | frev = self.rev | |
|
2970 | ||
|
2971 | prevrev = None | |
|
2972 | for request in requests: | |
|
2973 | node = request.node | |
|
2974 | rev = frev(node) | |
|
2975 | ||
|
2976 | if prevrev is None: | |
|
2977 | prevrev = self.index[rev][5] | |
|
2978 | ||
|
2979 | # Requesting a full revision. | |
|
2980 | if request.basenode == nullid: | |
|
2981 | baserev = nullrev | |
|
2982 | # Requesting an explicit revision. | |
|
2983 | elif request.basenode is not None: | |
|
2984 | baserev = frev(request.basenode) | |
|
2985 | # Allowing us to choose. | |
|
2986 | else: | |
|
2987 | p1rev, p2rev = self.parentrevs(rev) | |
|
2988 | deltaparentrev = self.deltaparent(rev) | |
|
2989 | ||
|
2990 | # Avoid sending full revisions when delta parent is null. Pick | |
|
2991 | # prev in that case. It's tempting to pick p1 in this case, as | |
|
2992 | # p1 will be smaller in the common case. However, computing a | |
|
2993 | # delta against p1 may require resolving the raw text of p1, | |
|
2994 | # which could be expensive. The revlog caches should have prev | |
|
2995 | # cached, meaning less CPU for delta generation. There is | |
|
2996 | # likely room to add a flag and/or config option to control this | |
|
2997 | # behavior. | |
|
2998 | if deltaparentrev == nullrev and self.storedeltachains: | |
|
2999 | baserev = prevrev | |
|
3000 | ||
|
3001 | # Revlog is configured to use full snapshot for a reason. | |
|
3002 | # Stick to full snapshot. | |
|
3003 | elif deltaparentrev == nullrev: | |
|
3004 | baserev = nullrev | |
|
3005 | ||
|
3006 | # Pick previous when we can't be sure the base is available | |
|
3007 | # on consumer. | |
|
3008 | elif deltaparentrev not in (p1rev, p2rev, prevrev): | |
|
3009 | baserev = prevrev | |
|
3010 | else: | |
|
3011 | baserev = deltaparentrev | |
|
3012 | ||
|
3013 | if baserev != nullrev and not self.candelta(baserev, rev): | |
|
3014 | baserev = nullrev | |
|
3015 | ||
|
3016 | revision = None | |
|
3017 | delta = None | |
|
3018 | baserevisionsize = None | |
|
3019 | ||
|
3020 | if self.iscensored(baserev) or self.iscensored(rev): | |
|
3021 | try: | |
|
3022 | revision = self.revision(node, raw=True) | |
|
3023 | except error.CensoredNodeError as e: | |
|
3024 | revision = e.tombstone | |
|
3025 | ||
|
3026 | if baserev != nullrev: | |
|
3027 | baserevisionsize = self.rawsize(baserev) | |
|
3028 | ||
|
3029 | elif baserev == nullrev: | |
|
3030 | revision = self.revision(node, raw=True) | |
|
3031 | else: | |
|
3032 | delta = self.revdiff(baserev, rev) | |
|
3033 | ||
|
3034 | extraflags = REVIDX_ELLIPSIS if request.ellipsis else 0 | |
|
3035 | ||
|
3036 | yield revlogrevisiondelta( | |
|
3037 | node=node, | |
|
3038 | p1node=request.p1node, | |
|
3039 | p2node=request.p2node, | |
|
3040 | linknode=request.linknode, | |
|
3041 | basenode=self.node(baserev), | |
|
3042 | flags=self.flags(rev) | extraflags, | |
|
3043 | baserevisionsize=baserevisionsize, | |
|
3044 | revision=revision, | |
|
3045 | delta=delta) | |
|
3046 | ||
|
3047 | prevrev = rev | |
|
3048 | ||
|
2953 | 3049 | DELTAREUSEALWAYS = 'always' |
|
2954 | 3050 | DELTAREUSESAMEREVS = 'samerevs' |
|
2955 | 3051 | DELTAREUSENEVER = 'never' |
@@ -22,6 +22,7 b' from mercurial.node import (' | |||
|
22 | 22 | nullrev, |
|
23 | 23 | ) |
|
24 | 24 | from mercurial.thirdparty import ( |
|
25 | attr, | |
|
25 | 26 | cbor, |
|
26 | 27 | ) |
|
27 | 28 | from mercurial import ( |
@@ -60,6 +61,19 b' def validaterev(rev):' | |||
|
60 | 61 | if not isinstance(rev, int): |
|
61 | 62 | raise ValueError('expected int') |
|
62 | 63 | |
|
64 | @interfaceutil.implementer(repository.irevisiondelta) | |
|
65 | @attr.s(slots=True, frozen=True) | |
|
66 | class simplestorerevisiondelta(object): | |
|
67 | node = attr.ib() | |
|
68 | p1node = attr.ib() | |
|
69 | p2node = attr.ib() | |
|
70 | basenode = attr.ib() | |
|
71 | linknode = attr.ib() | |
|
72 | flags = attr.ib() | |
|
73 | baserevisionsize = attr.ib() | |
|
74 | revision = attr.ib() | |
|
75 | delta = attr.ib() | |
|
76 | ||
|
63 | 77 | @interfaceutil.implementer(repository.ifilestorage) |
|
64 | 78 | class filestorage(object): |
|
65 | 79 | """Implements storage for a tracked path. |
@@ -500,6 +514,54 b' class filestorage(object):' | |||
|
500 | 514 | return mdiff.textdiff(self.revision(node1, raw=True), |
|
501 | 515 | self.revision(node2, raw=True)) |
|
502 | 516 | |
|
517 | def emitrevisiondeltas(self, requests): | |
|
518 | for request in requests: | |
|
519 | node = request.node | |
|
520 | rev = self.rev(node) | |
|
521 | ||
|
522 | if request.basenode == nullid: | |
|
523 | baserev = nullrev | |
|
524 | elif request.basenode is not None: | |
|
525 | baserev = self.rev(request.basenode) | |
|
526 | else: | |
|
527 | # This is a test extension and we can do simple things | |
|
528 | # for choosing a delta parent. | |
|
529 | baserev = self.deltaparent(rev) | |
|
530 | ||
|
531 | if baserev != nullrev and not self.candelta(baserev, rev): | |
|
532 | baserev = nullrev | |
|
533 | ||
|
534 | revision = None | |
|
535 | delta = None | |
|
536 | baserevisionsize = None | |
|
537 | ||
|
538 | if self.iscensored(baserev) or self.iscensored(rev): | |
|
539 | try: | |
|
540 | revision = self.revision(node, raw=True) | |
|
541 | except error.CensoredNodeError as e: | |
|
542 | revision = e.tombstone | |
|
543 | ||
|
544 | if baserev != nullrev: | |
|
545 | baserevisionsize = self.rawsize(baserev) | |
|
546 | ||
|
547 | elif baserev == nullrev: | |
|
548 | revision = self.revision(node, raw=True) | |
|
549 | else: | |
|
550 | delta = self.revdiff(baserev, rev) | |
|
551 | ||
|
552 | extraflags = revlog.REVIDX_ELLIPSIS if request.ellipsis else 0 | |
|
553 | ||
|
554 | yield simplestorerevisiondelta( | |
|
555 | node=node, | |
|
556 | p1node=request.p1node, | |
|
557 | p2node=request.p2node, | |
|
558 | linknode=request.linknode, | |
|
559 | basenode=self.node(baserev), | |
|
560 | flags=self.flags(rev) | extraflags, | |
|
561 | baserevisionsize=baserevisionsize, | |
|
562 | revision=revision, | |
|
563 | delta=delta) | |
|
564 | ||
|
503 | 565 | def headrevs(self): |
|
504 | 566 | # Assume all revisions are heads by default. |
|
505 | 567 | revishead = {rev: True for rev in self._indexbyrev} |
@@ -29,6 +29,7 b' from mercurial import (' | |||
|
29 | 29 | manifest, |
|
30 | 30 | pycompat, |
|
31 | 31 | repository, |
|
32 | revlog, | |
|
32 | 33 | sshpeer, |
|
33 | 34 | statichttprepo, |
|
34 | 35 | ui as uimod, |
@@ -198,11 +199,11 b' def main():' | |||
|
198 | 199 | checkzobject(mctx.read()) |
|
199 | 200 | |
|
200 | 201 | ziverify.verifyClass(repository.irevisiondelta, |
|
201 |
|
|
|
202 | revlog.revlogrevisiondelta) | |
|
202 | 203 | ziverify.verifyClass(repository.irevisiondeltarequest, |
|
203 | 204 | changegroup.revisiondeltarequest) |
|
204 | 205 | |
|
205 |
rd = |
|
|
206 | rd = revlog.revlogrevisiondelta( | |
|
206 | 207 | node=b'', |
|
207 | 208 | p1node=b'', |
|
208 | 209 | p2node=b'', |
General Comments 0
You need to be logged in to leave comments.
Login now