diff -r 4cd214c9948d -r a41565bef69f mercurial/changegroup.py --- a/mercurial/changegroup.py Fri Feb 19 11:07:10 2021 +0100 +++ b/mercurial/changegroup.py Thu Feb 18 17:36:52 2021 +0100 @@ -32,6 +32,7 @@ ) from .interfaces import repository +from .revlogutils import sidedata as sidedatamod _CHANGEGROUPV1_DELTA_HEADER = struct.Struct(b"20s20s20s20s") _CHANGEGROUPV2_DELTA_HEADER = struct.Struct(b"20s20s20s20s20s") @@ -202,7 +203,9 @@ header = self.deltaheader.unpack(headerdata) delta = readexactly(self._stream, l - self.deltaheadersize) node, p1, p2, deltabase, cs, flags = self._deltaheader(header, prevnode) - return (node, p1, p2, cs, deltabase, delta, flags) + # cg4 forward-compat + sidedata = {} + return (node, p1, p2, cs, deltabase, delta, flags, sidedata) def getchunks(self): """returns all the chunks contains in the bundle @@ -552,6 +555,29 @@ raise error.Abort(_(b"received dir revlog group is empty")) +class cg4unpacker(cg3unpacker): + """Unpacker for cg4 streams. + + cg4 streams add support for exchanging sidedata. + """ + + version = b'04' + + def deltachunk(self, prevnode): + res = super(cg4unpacker, self).deltachunk(prevnode) + if not res: + return res + + (node, p1, p2, cs, deltabase, delta, flags, _sidedata) = res + + sidedata_raw = getchunk(self._stream) + sidedata = {} + if len(sidedata_raw) > 0: + sidedata = sidedatamod.deserialize_sidedata(sidedata_raw) + + return node, p1, p2, cs, deltabase, delta, flags, sidedata + + class headerlessfixup(object): def __init__(self, fh, h): self._h = h @@ -861,6 +887,7 @@ shallow=False, ellipsisroots=None, fullnodes=None, + remote_sidedata=None, ): """Given a source repo, construct a bundler. @@ -893,6 +920,8 @@ nodes. We store this rather than the set of nodes that should be ellipsis because for very large histories we expect this to be significantly smaller. + + remote_sidedata is the set of sidedata categories wanted by the remote. """ assert oldmatcher assert matcher @@ -988,7 +1017,7 @@ for tree, deltas in it: if tree: - assert self.version == b'03' + assert self.version in (b'03', b'04') chunk = _fileheader(tree) size += len(chunk) yield chunk @@ -1394,6 +1423,7 @@ shallow=False, ellipsisroots=None, fullnodes=None, + remote_sidedata=None, ): builddeltaheader = lambda d: _CHANGEGROUPV1_DELTA_HEADER.pack( d.node, d.p1node, d.p2node, d.linknode @@ -1424,6 +1454,7 @@ shallow=False, ellipsisroots=None, fullnodes=None, + remote_sidedata=None, ): builddeltaheader = lambda d: _CHANGEGROUPV2_DELTA_HEADER.pack( d.node, d.p1node, d.p2node, d.basenode, d.linknode @@ -1453,6 +1484,7 @@ shallow=False, ellipsisroots=None, fullnodes=None, + remote_sidedata=None, ): builddeltaheader = lambda d: _CHANGEGROUPV3_DELTA_HEADER.pack( d.node, d.p1node, d.p2node, d.basenode, d.linknode, d.flags @@ -1473,12 +1505,47 @@ ) +def _makecg4packer( + repo, + oldmatcher, + matcher, + bundlecaps, + ellipses=False, + shallow=False, + ellipsisroots=None, + fullnodes=None, + remote_sidedata=None, +): + # Same header func as cg3. Sidedata is in a separate chunk from the delta to + # differenciate "raw delta" and sidedata. + builddeltaheader = lambda d: _CHANGEGROUPV3_DELTA_HEADER.pack( + d.node, d.p1node, d.p2node, d.basenode, d.linknode, d.flags + ) + + return cgpacker( + repo, + oldmatcher, + matcher, + b'04', + builddeltaheader=builddeltaheader, + manifestsend=closechunk(), + bundlecaps=bundlecaps, + ellipses=ellipses, + shallow=shallow, + ellipsisroots=ellipsisroots, + fullnodes=fullnodes, + remote_sidedata=remote_sidedata, + ) + + _packermap = { b'01': (_makecg1packer, cg1unpacker), # cg2 adds support for exchanging generaldelta b'02': (_makecg2packer, cg2unpacker), # cg3 adds support for exchanging revlog flags and treemanifests b'03': (_makecg3packer, cg3unpacker), + # ch4 adds support for exchanging sidedata + b'04': (_makecg4packer, cg4unpacker), } @@ -1498,11 +1565,9 @@ # # (or even to push subset of history) needv03 = True - if b'exp-sidedata-flag' in repo.requirements: - needv03 = True - # don't attempt to use 01/02 until we do sidedata cleaning - versions.discard(b'01') - versions.discard(b'02') + has_revlogv2 = requirements.REVLOGV2_REQUIREMENT in repo.requirements + if not has_revlogv2: + versions.discard(b'04') if not needv03: versions.discard(b'03') return versions @@ -1565,6 +1630,7 @@ shallow=False, ellipsisroots=None, fullnodes=None, + remote_sidedata=None, ): assert version in supportedoutgoingversions(repo) @@ -1601,6 +1667,7 @@ shallow=shallow, ellipsisroots=ellipsisroots, fullnodes=fullnodes, + remote_sidedata=remote_sidedata, ) @@ -1644,8 +1711,15 @@ fastpath=False, bundlecaps=None, matcher=None, + remote_sidedata=None, ): - bundler = getbundler(version, repo, bundlecaps=bundlecaps, matcher=matcher) + bundler = getbundler( + version, + repo, + bundlecaps=bundlecaps, + matcher=matcher, + remote_sidedata=remote_sidedata, + ) repo = repo.unfiltered() commonrevs = outgoing.common