diff --git a/mercurial/changegroup.py b/mercurial/changegroup.py --- a/mercurial/changegroup.py +++ b/mercurial/changegroup.py @@ -588,11 +588,37 @@ key = lambda n: cl.rev(lookup(n)) return [store.rev(n) for n in sorted(nodes, key=key)] -def _revisiondeltanormal(store, rev, prev, linknode, deltaparentfn): +def _revisiondeltanormal(store, rev, prev, linknode, forcedeltaparentprev): """Construct a revision delta for non-ellipses changegroup generation.""" node = store.node(rev) p1, p2 = store.parentrevs(rev) - base = deltaparentfn(store, rev, p1, p2, prev) + + if forcedeltaparentprev: + base = prev + else: + dp = store.deltaparent(rev) + + if dp == nullrev and store.storedeltachains: + # Avoid sending full revisions when delta parent is null. Pick prev + # in that case. It's tempting to pick p1 in this case, as p1 will + # be smaller in the common case. However, computing a delta against + # p1 may require resolving the raw text of p1, which could be + # expensive. The revlog caches should have prev cached, meaning + # less CPU for changegroup generation. There is likely room to add + # a flag and/or config option to control this behavior. + base = prev + elif dp == nullrev: + # revlog is configured to use full snapshot for a reason, + # stick to full snapshot. + base = nullrev + elif dp not in (p1, p2, prev): + # Pick prev when we can't be sure remote has the base revision. + base = prev + else: + base = dp + + if base != nullrev and not store.candelta(base, rev): + base = nullrev revision = None delta = None @@ -720,7 +746,7 @@ delta=None, ) -def deltagroup(repo, revs, store, ischangelog, lookup, deltaparentfn, +def deltagroup(repo, revs, store, ischangelog, lookup, forcedeltaparentprev, units=None, ellipses=False, clrevtolocalrev=None, fullclnodes=None, precomputedellipsis=None): @@ -762,7 +788,7 @@ # corresponds to was a full changeset. if linknode in fullclnodes: delta = _revisiondeltanormal(store, curr, prev, linknode, - deltaparentfn) + forcedeltaparentprev) elif linkrev not in precomputedellipsis: delta = None else: @@ -772,7 +798,7 @@ precomputedellipsis) else: delta = _revisiondeltanormal(store, curr, prev, linknode, - deltaparentfn) + forcedeltaparentprev) if delta: yield delta @@ -782,7 +808,8 @@ class cgpacker(object): def __init__(self, repo, filematcher, version, allowreorder, - deltaparentfn, builddeltaheader, manifestsend, + builddeltaheader, manifestsend, + forcedeltaparentprev=False, bundlecaps=None, ellipses=False, shallow=False, ellipsisroots=None, fullnodes=None): """Given a source repo, construct a bundler. @@ -794,8 +821,9 @@ This value is used when ``bundle.reorder`` is ``auto`` or isn't set. - deltaparentfn is a callable that resolves the delta parent for - a specific revision. + forcedeltaparentprev indicates whether delta parents must be against + the previous revision in a delta group. This should only be used for + compatibility with changegroup version 1. builddeltaheader is a callable that constructs the header for a group delta. @@ -821,7 +849,7 @@ self._filematcher = filematcher self.version = version - self._deltaparentfn = deltaparentfn + self._forcedeltaparentprev = forcedeltaparentprev self._builddeltaheader = builddeltaheader self._manifestsend = manifestsend self._ellipses = ellipses @@ -1026,7 +1054,7 @@ gen = deltagroup( self._repo, revs, cl, True, lookupcl, - self._deltaparentfn, + self._forcedeltaparentprev, ellipses=self._ellipses, units=_('changesets'), clrevtolocalrev={}, @@ -1115,7 +1143,7 @@ deltas = deltagroup( self._repo, revs, store, False, lookupfn, - self._deltaparentfn, + self._forcedeltaparentprev, ellipses=self._ellipses, units=_('manifests'), clrevtolocalrev=clrevtolocalrev, @@ -1207,7 +1235,7 @@ deltas = deltagroup( self._repo, revs, filerevlog, False, lookupfilelog, - self._deltaparentfn, + self._forcedeltaparentprev, ellipses=self._ellipses, clrevtolocalrev=clrevtolocalrev, fullclnodes=self._fullclnodes, @@ -1217,65 +1245,16 @@ progress.complete() -def _deltaparentprev(store, rev, p1, p2, prev): - """Resolve a delta parent to the previous revision. - - Used for version 1 changegroups, which don't support generaldelta. - """ - return prev - -def _deltaparentgeneraldelta(store, rev, p1, p2, prev): - """Resolve a delta parent when general deltas are supported.""" - dp = store.deltaparent(rev) - if dp == nullrev and store.storedeltachains: - # Avoid sending full revisions when delta parent is null. Pick prev - # in that case. It's tempting to pick p1 in this case, as p1 will - # be smaller in the common case. However, computing a delta against - # p1 may require resolving the raw text of p1, which could be - # expensive. The revlog caches should have prev cached, meaning - # less CPU for changegroup generation. There is likely room to add - # a flag and/or config option to control this behavior. - base = prev - elif dp == nullrev: - # revlog is configured to use full snapshot for a reason, - # stick to full snapshot. - base = nullrev - elif dp not in (p1, p2, prev): - # Pick prev when we can't be sure remote has the base revision. - return prev - else: - base = dp - - if base != nullrev and not store.candelta(base, rev): - base = nullrev - - return base - -def _deltaparentellipses(store, rev, p1, p2, prev): - """Resolve a delta parent when in ellipses mode.""" - # TODO: send better deltas when in narrow mode. - # - # changegroup.group() loops over revisions to send, - # including revisions we'll skip. What this means is that - # `prev` will be a potentially useless delta base for all - # ellipsis nodes, as the client likely won't have it. In - # the future we should do bookkeeping about which nodes - # have been sent to the client, and try to be - # significantly smarter about delta bases. This is - # slightly tricky because this same code has to work for - # all revlogs, and we don't have the linkrev/linknode here. - return p1 - def _makecg1packer(repo, filematcher, bundlecaps, ellipses=False, shallow=False, ellipsisroots=None, fullnodes=None): builddeltaheader = lambda d: _CHANGEGROUPV1_DELTA_HEADER.pack( d.node, d.p1node, d.p2node, d.linknode) return cgpacker(repo, filematcher, b'01', - deltaparentfn=_deltaparentprev, allowreorder=None, builddeltaheader=builddeltaheader, manifestsend=b'', + forcedeltaparentprev=True, bundlecaps=bundlecaps, ellipses=ellipses, shallow=shallow, @@ -1291,7 +1270,6 @@ # generally doesn't help, so we disable it by default (treating # bundle.reorder=auto just like bundle.reorder=False). return cgpacker(repo, filematcher, b'02', - deltaparentfn=_deltaparentgeneraldelta, allowreorder=False, builddeltaheader=builddeltaheader, manifestsend=b'', @@ -1306,11 +1284,7 @@ builddeltaheader = lambda d: _CHANGEGROUPV3_DELTA_HEADER.pack( d.node, d.p1node, d.p2node, d.basenode, d.linknode, d.flags) - deltaparentfn = (_deltaparentellipses if ellipses - else _deltaparentgeneraldelta) - return cgpacker(repo, filematcher, b'03', - deltaparentfn=deltaparentfn, allowreorder=False, builddeltaheader=builddeltaheader, manifestsend=closechunk(),