Show More
@@ -31,7 +31,6 b' from . import (' | |||||
31 | phases, |
|
31 | phases, | |
32 | pycompat, |
|
32 | pycompat, | |
33 | repository, |
|
33 | repository, | |
34 | revlog, |
|
|||
35 | util, |
|
34 | util, | |
36 | ) |
|
35 | ) | |
37 |
|
36 | |||
@@ -512,19 +511,6 b' class revisiondeltarequest(object):' | |||||
512 | basenode = attr.ib() |
|
511 | basenode = attr.ib() | |
513 | ellipsis = attr.ib(default=False) |
|
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 | def _revisiondeltatochunks(delta, headerfn): |
|
514 | def _revisiondeltatochunks(delta, headerfn): | |
529 | """Serialize a revisiondelta to changegroup chunks.""" |
|
515 | """Serialize a revisiondelta to changegroup chunks.""" | |
530 |
|
516 | |||
@@ -583,77 +569,6 b' def _sortnodesellipsis(store, nodes, cl,' | |||||
583 | key = lambda n: cl.rev(lookup(n)) |
|
569 | key = lambda n: cl.rev(lookup(n)) | |
584 | return [store.rev(n) for n in sorted(nodes, key=key)] |
|
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 | def _makenarrowdeltarequest(cl, store, ischangelog, rev, node, linkrev, |
|
572 | def _makenarrowdeltarequest(cl, store, ischangelog, rev, node, linkrev, | |
658 | linknode, clrevtolocalrev, fullclnodes, |
|
573 | linknode, clrevtolocalrev, fullclnodes, | |
659 | precomputedellipsis): |
|
574 | precomputedellipsis): | |
@@ -832,17 +747,12 b' def deltagroup(repo, store, nodes, ischa' | |||||
832 | progress = repo.ui.makeprogress(_('bundling'), unit=units, |
|
747 | progress = repo.ui.makeprogress(_('bundling'), unit=units, | |
833 | total=len(requests)) |
|
748 | total=len(requests)) | |
834 |
|
749 | |||
835 | prevnode = store.node(revs[0]) |
|
750 | for i, delta in enumerate(store.emitrevisiondeltas(requests)): | |
836 | for i, request in enumerate(requests): |
|
|||
837 | if progress: |
|
751 | if progress: | |
838 | progress.update(i + 1) |
|
752 | progress.update(i + 1) | |
839 |
|
753 | |||
840 | delta = _handlerevisiondeltarequest(store, request, prevnode) |
|
|||
841 |
|
||||
842 | yield delta |
|
754 | yield delta | |
843 |
|
755 | |||
844 | prevnode = request.node |
|
|||
845 |
|
||||
846 | if progress: |
|
756 | if progress: | |
847 | progress.complete() |
|
757 | progress.complete() | |
848 |
|
758 |
@@ -95,6 +95,9 b' class filelog(object):' | |||||
95 | def revdiff(self, rev1, rev2): |
|
95 | def revdiff(self, rev1, rev2): | |
96 | return self._revlog.revdiff(rev1, rev2) |
|
96 | return self._revlog.revdiff(rev1, rev2) | |
97 |
|
97 | |||
|
98 | def emitrevisiondeltas(self, requests): | |||
|
99 | return self._revlog.emitrevisiondeltas(requests) | |||
|
100 | ||||
98 | def addrevision(self, revisiondata, transaction, linkrev, p1, p2, |
|
101 | def addrevision(self, revisiondata, transaction, linkrev, p1, p2, | |
99 | node=None, flags=revlog.REVIDX_DEFAULT_FLAGS, |
|
102 | node=None, flags=revlog.REVIDX_DEFAULT_FLAGS, | |
100 | cachedelta=None): |
|
103 | cachedelta=None): |
@@ -621,6 +621,30 b' class ifiledata(interfaceutil.Interface)' | |||||
621 | revision data. |
|
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 | class ifilemutation(interfaceutil.Interface): |
|
648 | class ifilemutation(interfaceutil.Interface): | |
625 | """Storage interface for mutation events of a tracked file.""" |
|
649 | """Storage interface for mutation events of a tracked file.""" | |
626 |
|
650 |
@@ -45,10 +45,12 b' from . import (' | |||||
45 | mdiff, |
|
45 | mdiff, | |
46 | policy, |
|
46 | policy, | |
47 | pycompat, |
|
47 | pycompat, | |
|
48 | repository, | |||
48 | templatefilters, |
|
49 | templatefilters, | |
49 | util, |
|
50 | util, | |
50 | ) |
|
51 | ) | |
51 | from .utils import ( |
|
52 | from .utils import ( | |
|
53 | interfaceutil, | |||
52 | stringutil, |
|
54 | stringutil, | |
53 | ) |
|
55 | ) | |
54 |
|
56 | |||
@@ -821,6 +823,19 b' class _revisioninfo(object):' | |||||
821 | cachedelta = attr.ib() |
|
823 | cachedelta = attr.ib() | |
822 | flags = attr.ib() |
|
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 | # index v0: |
|
839 | # index v0: | |
825 | # 4 bytes: offset |
|
840 | # 4 bytes: offset | |
826 | # 4 bytes: compressed length |
|
841 | # 4 bytes: compressed length | |
@@ -2950,6 +2965,87 b' class revlog(object):' | |||||
2950 | res.append(self.datafile) |
|
2965 | res.append(self.datafile) | |
2951 | return res |
|
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 | DELTAREUSEALWAYS = 'always' |
|
3049 | DELTAREUSEALWAYS = 'always' | |
2954 | DELTAREUSESAMEREVS = 'samerevs' |
|
3050 | DELTAREUSESAMEREVS = 'samerevs' | |
2955 | DELTAREUSENEVER = 'never' |
|
3051 | DELTAREUSENEVER = 'never' |
@@ -22,6 +22,7 b' from mercurial.node import (' | |||||
22 | nullrev, |
|
22 | nullrev, | |
23 | ) |
|
23 | ) | |
24 | from mercurial.thirdparty import ( |
|
24 | from mercurial.thirdparty import ( | |
|
25 | attr, | |||
25 | cbor, |
|
26 | cbor, | |
26 | ) |
|
27 | ) | |
27 | from mercurial import ( |
|
28 | from mercurial import ( | |
@@ -60,6 +61,19 b' def validaterev(rev):' | |||||
60 | if not isinstance(rev, int): |
|
61 | if not isinstance(rev, int): | |
61 | raise ValueError('expected int') |
|
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 | @interfaceutil.implementer(repository.ifilestorage) |
|
77 | @interfaceutil.implementer(repository.ifilestorage) | |
64 | class filestorage(object): |
|
78 | class filestorage(object): | |
65 | """Implements storage for a tracked path. |
|
79 | """Implements storage for a tracked path. | |
@@ -500,6 +514,54 b' class filestorage(object):' | |||||
500 | return mdiff.textdiff(self.revision(node1, raw=True), |
|
514 | return mdiff.textdiff(self.revision(node1, raw=True), | |
501 | self.revision(node2, raw=True)) |
|
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 | def headrevs(self): |
|
565 | def headrevs(self): | |
504 | # Assume all revisions are heads by default. |
|
566 | # Assume all revisions are heads by default. | |
505 | revishead = {rev: True for rev in self._indexbyrev} |
|
567 | revishead = {rev: True for rev in self._indexbyrev} |
@@ -29,6 +29,7 b' from mercurial import (' | |||||
29 | manifest, |
|
29 | manifest, | |
30 | pycompat, |
|
30 | pycompat, | |
31 | repository, |
|
31 | repository, | |
|
32 | revlog, | |||
32 | sshpeer, |
|
33 | sshpeer, | |
33 | statichttprepo, |
|
34 | statichttprepo, | |
34 | ui as uimod, |
|
35 | ui as uimod, | |
@@ -198,11 +199,11 b' def main():' | |||||
198 | checkzobject(mctx.read()) |
|
199 | checkzobject(mctx.read()) | |
199 |
|
200 | |||
200 | ziverify.verifyClass(repository.irevisiondelta, |
|
201 | ziverify.verifyClass(repository.irevisiondelta, | |
201 |
|
|
202 | revlog.revlogrevisiondelta) | |
202 | ziverify.verifyClass(repository.irevisiondeltarequest, |
|
203 | ziverify.verifyClass(repository.irevisiondeltarequest, | |
203 | changegroup.revisiondeltarequest) |
|
204 | changegroup.revisiondeltarequest) | |
204 |
|
205 | |||
205 |
rd = |
|
206 | rd = revlog.revlogrevisiondelta( | |
206 | node=b'', |
|
207 | node=b'', | |
207 | p1node=b'', |
|
208 | p1node=b'', | |
208 | p2node=b'', |
|
209 | p2node=b'', |
General Comments 0
You need to be logged in to leave comments.
Login now