diff -r db5501d93bcf -r 5a9ab91e0a45 mercurial/revlog.py --- a/mercurial/revlog.py Mon Sep 24 09:41:42 2018 -0700 +++ b/mercurial/revlog.py Fri Sep 21 14:28:21 2018 -0700 @@ -59,6 +59,7 @@ ) from . import ( ancestor, + dagop, error, mdiff, policy, @@ -242,17 +243,17 @@ flags = attr.ib() @interfaceutil.implementer(repository.irevisiondelta) -@attr.s(slots=True, frozen=True) +@attr.s(slots=True) class revlogrevisiondelta(object): node = attr.ib() p1node = attr.ib() p2node = attr.ib() basenode = attr.ib() - linknode = attr.ib() flags = attr.ib() baserevisionsize = attr.ib() revision = attr.ib() delta = attr.ib() + linknode = attr.ib(default=None) @interfaceutil.implementer(repository.iverifyproblem) @attr.s(frozen=True) @@ -2374,6 +2375,122 @@ prevrev = rev + def emitrevisions(self, nodes, nodesorder=None, revisiondata=False, + assumehaveparentrevisions=False, deltaprevious=False): + if nodesorder not in ('nodes', 'storage', None): + raise error.ProgrammingError('unhandled value for nodesorder: %s' % + nodesorder) + + if nodesorder is None and not self._generaldelta: + nodesorder = 'storage' + + frev = self.rev + fnode = self.node + + if nodesorder == 'nodes': + revs = [frev(n) for n in nodes] + elif nodesorder == 'storage': + revs = sorted(frev(n) for n in nodes) + else: + assert self._generaldelta + revs = set(frev(n) for n in nodes) + revs = dagop.linearize(revs, self.parentrevs) + + prevrev = None + + if deltaprevious or assumehaveparentrevisions: + prevrev = self.parentrevs(revs[0])[0] + + # Set of revs available to delta against. + available = set() + + for rev in revs: + if rev == nullrev: + continue + + node = fnode(rev) + deltaparentrev = self.deltaparent(rev) + p1rev, p2rev = self.parentrevs(rev) + + # Forced delta against previous mode. + if deltaprevious: + baserev = prevrev + + # Revlog is configured to use full snapshots. Stick to that. + elif not self._storedeltachains: + baserev = nullrev + + # There is a delta in storage. We try to use that because it + # amounts to effectively copying data from storage and is + # therefore the fastest. + elif deltaparentrev != nullrev: + # Base revision was already emitted in this group. We can + # always safely use the delta. + if deltaparentrev in available: + baserev = deltaparentrev + + # Base revision is a parent that hasn't been emitted already. + # Use it if we can assume the receiver has the parent revision. + elif (assumehaveparentrevisions + and deltaparentrev in (p1rev, p2rev)): + baserev = deltaparentrev + + # No guarantee the receiver has the delta parent. Send delta + # against last revision (if possible), which in the common case + # should be similar enough to this revision that the delta is + # reasonable. + elif prevrev is not None: + baserev = prevrev + else: + baserev = nullrev + + # Storage has a fulltext revision. + + # Let's use the previous revision, which is as good a guess as any. + # There is definitely room to improve this logic. + elif prevrev is not None: + baserev = prevrev + else: + baserev = nullrev + + # But we can't actually use our chosen delta base for whatever + # reason. Reset to fulltext. + if baserev != nullrev and not self.candelta(baserev, rev): + baserev = nullrev + + revision = None + delta = None + baserevisionsize = None + + if revisiondata: + if self.iscensored(baserev) or self.iscensored(rev): + try: + revision = self.revision(node, raw=True) + except error.CensoredNodeError as e: + revision = e.tombstone + + if baserev != nullrev: + baserevisionsize = self.rawsize(baserev) + + elif baserev == nullrev and not deltaprevious: + revision = self.revision(node, raw=True) + available.add(rev) + else: + delta = self.revdiff(baserev, rev) + available.add(rev) + + yield revlogrevisiondelta( + node=node, + p1node=fnode(p1rev), + p2node=fnode(p2rev), + basenode=fnode(baserev), + flags=self.flags(rev), + baserevisionsize=baserevisionsize, + revision=revision, + delta=delta) + + prevrev = rev + DELTAREUSEALWAYS = 'always' DELTAREUSESAMEREVS = 'samerevs' DELTAREUSENEVER = 'never'