comparison mercurial/revlog.py @ 39231:b41d023a412a

repository: establish API for emitting revision deltas With our revision delta and revision delta request interfaces defined, it is now time to define a method on storage interfaces for using them. So far, the only storage interface that is well-defined and used is file storage. So that is the only interface we need to add a method on. We define an ``emitrevisiondeltas()`` method that takes an iterable of ``irevisiondeltarequest``s and turns them into ``irevisiondelta`` instances. changegroup._handlerevisiondeltarequest() and the looping logic from changegroup.deltagroup() has effectively been moved to revlog.emitrevisiondeltas(). Our filelog wrapper class proxies its emitrevisiondeltas() to the internal revlog instance. The simple store test extension used to verify sanity of storage abstractions has also implemented emitrevisiondeltas() for file storage and the test harness when run with this extension doesn't seem to exhibit any regressions. Rather than create a shared type to represent revision deltas, each storage backend has its own type and the class name identifies where the revision delta was derived from. Differential Revision: https://phab.mercurial-scm.org/D4226
author Gregory Szorc <gregory.szorc@gmail.com>
date Thu, 09 Aug 2018 16:02:14 -0700
parents dbc5ead9f40f
children 0a5b20c107a6
comparison
equal deleted inserted replaced
39230:b518d495a560 39231:b41d023a412a
43 ancestor, 43 ancestor,
44 error, 44 error,
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
55 parsers = policy.importmod(r'parsers') 57 parsers = policy.importmod(r'parsers')
56 58
818 p2 = attr.ib() 820 p2 = attr.ib()
819 btext = attr.ib() 821 btext = attr.ib()
820 textlen = attr.ib() 822 textlen = attr.ib()
821 cachedelta = attr.ib() 823 cachedelta = attr.ib()
822 flags = attr.ib() 824 flags = attr.ib()
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()
823 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
827 # 4 bytes: base rev 842 # 4 bytes: base rev
2948 res = [self.indexfile] 2963 res = [self.indexfile]
2949 if not self._inline: 2964 if not self._inline:
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'
2956 3052
2957 DELTAREUSEFULLADD = 'fulladd' 3053 DELTAREUSEFULLADD = 'fulladd'