# HG changeset patch # User Raphaël Gomès # Date 2021-02-18 16:36:52 # Node ID a41565bef69f7e0224a15dff8f381f1a77d0da66 # Parent 4cd214c9948d40d006fc09df0c20f090f387d426 changegroup: add v4 changegroup for revlog v2 exchange This change only adds the required infrastructure for the new changegroup format and does not do any actual exchange. This will be done in the next patches. Differential Revision: https://phab.mercurial-scm.org/D10026 diff --git a/hgext/remotefilelog/remotefilelog.py b/hgext/remotefilelog/remotefilelog.py --- a/hgext/remotefilelog/remotefilelog.py +++ b/hgext/remotefilelog/remotefilelog.py @@ -306,6 +306,7 @@ class remotefilelog(object): assumehaveparentrevisions=False, deltaprevious=False, deltamode=None, + sidedata_helpers=None, ): # we don't use any of these parameters here del nodesorder, revisiondata, assumehaveparentrevisions, deltaprevious @@ -333,6 +334,8 @@ class remotefilelog(object): baserevisionsize=None, revision=revision, delta=delta, + # Sidedata is not supported yet + sidedata=None, ) def revdiff(self, node1, node2): diff --git a/hgext/remotefilelog/shallowbundle.py b/hgext/remotefilelog/shallowbundle.py --- a/hgext/remotefilelog/shallowbundle.py +++ b/hgext/remotefilelog/shallowbundle.py @@ -67,7 +67,7 @@ class shallowcg1packer(changegroup.cgpac shallowcg1packer, self, nodelist, rlog, lookup, units=units ) - def generatefiles(self, changedfiles, *args): + def generatefiles(self, changedfiles, *args, **kwargs): try: linknodes, commonrevs, source = args except ValueError: @@ -92,7 +92,9 @@ class shallowcg1packer(changegroup.cgpac [f for f in changedfiles if not repo.shallowmatch(f)] ) - return super(shallowcg1packer, self).generatefiles(changedfiles, *args) + return super(shallowcg1packer, self).generatefiles( + changedfiles, *args, **kwargs + ) def shouldaddfilegroups(self, source): repo = self._repo @@ -176,9 +178,11 @@ def makechangegroup(orig, repo, outgoing repo.shallowmatch = original -def addchangegroupfiles(orig, repo, source, revmap, trp, expectedfiles, *args): +def addchangegroupfiles( + orig, repo, source, revmap, trp, expectedfiles, *args, **kwargs +): if not shallowutil.isenabled(repo): - return orig(repo, source, revmap, trp, expectedfiles, *args) + return orig(repo, source, revmap, trp, expectedfiles, *args, **kwargs) newfiles = 0 visited = set() @@ -272,7 +276,7 @@ def addchangegroupfiles(orig, repo, sour revisiondata = revisiondatas[(f, node)] # revisiondata: (node, p1, p2, cs, deltabase, delta, flags) - node, p1, p2, linknode, deltabase, delta, flags = revisiondata + node, p1, p2, linknode, deltabase, delta, flags, sidedata = revisiondata if not available(f, node, f, deltabase): continue diff --git a/hgext/sqlitestore.py b/hgext/sqlitestore.py --- a/hgext/sqlitestore.py +++ b/hgext/sqlitestore.py @@ -681,7 +681,16 @@ class sqlitefilestore(object): ): empty = True - for node, p1, p2, linknode, deltabase, delta, wireflags in deltas: + for ( + node, + p1, + p2, + linknode, + deltabase, + delta, + wireflags, + sidedata, + ) in deltas: storeflags = 0 if wireflags & repository.REVISION_FLAG_CENSORED: diff --git a/mercurial/bundlerepo.py b/mercurial/bundlerepo.py --- a/mercurial/bundlerepo.py +++ b/mercurial/bundlerepo.py @@ -61,7 +61,7 @@ class bundlerevlog(revlog.revlog): self.repotiprev = n - 1 self.bundlerevs = set() # used by 'bundle()' revset expression for deltadata in cgunpacker.deltaiter(): - node, p1, p2, cs, deltabase, delta, flags = deltadata + node, p1, p2, cs, deltabase, delta, flags, sidedata = deltadata size = len(delta) start = cgunpacker.tell() - size diff --git a/mercurial/changegroup.py b/mercurial/changegroup.py --- a/mercurial/changegroup.py +++ b/mercurial/changegroup.py @@ -32,6 +32,7 @@ from . import ( ) 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 @@ class cg1unpacker(object): 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 @@ class cg3unpacker(cg2unpacker): 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 @@ class cgpacker(object): shallow=False, ellipsisroots=None, fullnodes=None, + remote_sidedata=None, ): """Given a source repo, construct a bundler. @@ -893,6 +920,8 @@ class cgpacker(object): 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 @@ class cgpacker(object): 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 @@ def _makecg1packer( 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 @@ def _makecg2packer( 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 @@ def _makecg3packer( 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 _makecg3packer( ) +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 @@ def allsupportedversions(repo): # # (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 @@ def getbundler( shallow=False, ellipsisroots=None, fullnodes=None, + remote_sidedata=None, ): assert version in supportedoutgoingversions(repo) @@ -1601,6 +1667,7 @@ def getbundler( shallow=shallow, ellipsisroots=ellipsisroots, fullnodes=fullnodes, + remote_sidedata=remote_sidedata, ) @@ -1644,8 +1711,15 @@ def makestream( 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 diff --git a/mercurial/debugcommands.py b/mercurial/debugcommands.py --- a/mercurial/debugcommands.py +++ b/mercurial/debugcommands.py @@ -346,7 +346,7 @@ def _debugchangegroup(ui, gen, all=None, def showchunks(named): ui.write(b"\n%s%s\n" % (indent_string, named)) for deltadata in gen.deltaiter(): - node, p1, p2, cs, deltabase, delta, flags = deltadata + node, p1, p2, cs, deltabase, delta, flags, sidedata = deltadata ui.write( b"%s%s %s %s %s %s %d\n" % ( @@ -372,7 +372,7 @@ def _debugchangegroup(ui, gen, all=None, raise error.Abort(_(b'use debugbundle2 for this file')) gen.changelogheader() for deltadata in gen.deltaiter(): - node, p1, p2, cs, deltabase, delta, flags = deltadata + node, p1, p2, cs, deltabase, delta, flags, sidedata = deltadata ui.write(b"%s%s\n" % (indent_string, hex(node))) diff --git a/mercurial/exchange.py b/mercurial/exchange.py --- a/mercurial/exchange.py +++ b/mercurial/exchange.py @@ -2249,7 +2249,13 @@ def bundle2requested(bundlecaps): def getbundlechunks( - repo, source, heads=None, common=None, bundlecaps=None, **kwargs + repo, + source, + heads=None, + common=None, + bundlecaps=None, + remote_sidedata=None, + **kwargs ): """Return chunks constituting a bundle's raw data. @@ -2279,7 +2285,12 @@ def getbundlechunks( return ( info, changegroup.makestream( - repo, outgoing, b'01', source, bundlecaps=bundlecaps + repo, + outgoing, + b'01', + source, + bundlecaps=bundlecaps, + remote_sidedata=remote_sidedata, ), ) @@ -2303,6 +2314,7 @@ def getbundlechunks( source, bundlecaps=bundlecaps, b2caps=b2caps, + remote_sidedata=remote_sidedata, **pycompat.strkwargs(kwargs) ) @@ -2325,6 +2337,7 @@ def _getbundlechangegrouppart( b2caps=None, heads=None, common=None, + remote_sidedata=None, **kwargs ): """add a changegroup part to the requested bundle""" @@ -2355,7 +2368,13 @@ def _getbundlechangegrouppart( matcher = None cgstream = changegroup.makestream( - repo, outgoing, version, source, bundlecaps=bundlecaps, matcher=matcher + repo, + outgoing, + version, + source, + bundlecaps=bundlecaps, + matcher=matcher, + remote_sidedata=remote_sidedata, ) part = bundler.newpart(b'changegroup', data=cgstream) diff --git a/mercurial/exchangev2.py b/mercurial/exchangev2.py --- a/mercurial/exchangev2.py +++ b/mercurial/exchangev2.py @@ -417,6 +417,8 @@ def _processchangesetdata(repo, tr, objs mdiff.trivialdiffheader(len(data)) + data, # Flags not yet supported. 0, + # Sidedata not yet supported + {}, ) cl.addgroup( @@ -496,6 +498,8 @@ def _fetchmanifests(repo, tr, remote, ma delta, # Flags not yet supported. 0, + # Sidedata not yet supported. + {}, ) progress.increment() @@ -621,6 +625,8 @@ def _fetchfiles(repo, tr, remote, fnodes delta, # Flags not yet supported. 0, + # Sidedata not yet supported. + {}, ) progress.increment() @@ -719,6 +725,8 @@ def _fetchfilesfromcsets( delta, # Flags not yet supported. 0, + # Sidedata not yet supported. + {}, ) progress.increment() diff --git a/mercurial/localrepo.py b/mercurial/localrepo.py --- a/mercurial/localrepo.py +++ b/mercurial/localrepo.py @@ -316,7 +316,13 @@ class localpeer(repository.peer): ) def getbundle( - self, source, heads=None, common=None, bundlecaps=None, **kwargs + self, + source, + heads=None, + common=None, + bundlecaps=None, + remote_sidedata=None, + **kwargs ): chunks = exchange.getbundlechunks( self._repo, @@ -324,6 +330,7 @@ class localpeer(repository.peer): heads=heads, common=common, bundlecaps=bundlecaps, + remote_sidedata=remote_sidedata, **kwargs )[1] cb = util.chunkbuffer(chunks) diff --git a/mercurial/revlog.py b/mercurial/revlog.py --- a/mercurial/revlog.py +++ b/mercurial/revlog.py @@ -2527,7 +2527,7 @@ class revlog(object): deltacomputer = deltautil.deltacomputer(self) # loop through our set of deltas for data in deltas: - node, p1, p2, linknode, deltabase, delta, flags = data + node, p1, p2, linknode, deltabase, delta, flags, sidedata = data link = linkmapper(linknode) flags = flags or REVIDX_DEFAULT_FLAGS diff --git a/tests/test-check-interfaces.py b/tests/test-check-interfaces.py --- a/tests/test-check-interfaces.py +++ b/tests/test-check-interfaces.py @@ -276,6 +276,7 @@ def main(): flags=b'', baserevisionsize=None, revision=b'', + sidedata=b'', delta=None, ) checkzobject(rd) diff --git a/tests/test-revlog-raw.py b/tests/test-revlog-raw.py --- a/tests/test-revlog-raw.py +++ b/tests/test-revlog-raw.py @@ -147,6 +147,7 @@ def addgroupcopy(rlog, tr, destname=b'_d b'flags': rlog.flags(r), b'deltabase': rlog.node(deltaparent), b'delta': rlog.revdiff(deltaparent, r), + b'sidedata': rlog.sidedata(r), } def deltaiter(self): @@ -159,10 +160,11 @@ def addgroupcopy(rlog, tr, destname=b'_d deltabase = chunkdata[b'deltabase'] delta = chunkdata[b'delta'] flags = chunkdata[b'flags'] + sidedata = chunkdata[b'sidedata'] chain = node - yield (node, p1, p2, cs, deltabase, delta, flags) + yield (node, p1, p2, cs, deltabase, delta, flags, sidedata) def linkmap(lnode): return rlog.rev(lnode)