comparison mercurial/changegroup.py @ 38904:cdb9bc216771

changegroup: declare shallow flag in constructor Thus begins the process of better formalizing ellipses and shallow changegroup generation mode so it is tracked by cgpacker at construction time instead of bolted on after the fact by a wrapper function. Differential Revision: https://phab.mercurial-scm.org/D4086
author Gregory Szorc <gregory.szorc@gmail.com>
date Fri, 03 Aug 2018 12:57:11 -0700
parents 68e490ed640e
children 379d90327861
comparison
equal deleted inserted replaced
38903:68e490ed640e 38904:cdb9bc216771
520 deltachunks = attr.ib() 520 deltachunks = attr.ib()
521 521
522 class cgpacker(object): 522 class cgpacker(object):
523 def __init__(self, repo, filematcher, version, allowreorder, 523 def __init__(self, repo, filematcher, version, allowreorder,
524 useprevdelta, builddeltaheader, manifestsend, 524 useprevdelta, builddeltaheader, manifestsend,
525 sendtreemanifests, bundlecaps=None): 525 sendtreemanifests, bundlecaps=None, shallow=False):
526 """Given a source repo, construct a bundler. 526 """Given a source repo, construct a bundler.
527 527
528 filematcher is a matcher that matches on files to include in the 528 filematcher is a matcher that matches on files to include in the
529 changegroup. Used to facilitate sparse changegroups. 529 changegroup. Used to facilitate sparse changegroups.
530 530
544 544
545 bundlecaps is optional and can be used to specify the set of 545 bundlecaps is optional and can be used to specify the set of
546 capabilities which can be used to build the bundle. While bundlecaps is 546 capabilities which can be used to build the bundle. While bundlecaps is
547 unused in core Mercurial, extensions rely on this feature to communicate 547 unused in core Mercurial, extensions rely on this feature to communicate
548 capabilities to customize the changegroup packer. 548 capabilities to customize the changegroup packer.
549
550 shallow indicates whether shallow data might be sent. The packer may
551 need to pack file contents not introduced by the changes being packed.
549 """ 552 """
550 assert filematcher 553 assert filematcher
551 self._filematcher = filematcher 554 self._filematcher = filematcher
552 555
553 self.version = version 556 self.version = version
558 561
559 # Set of capabilities we can use to build the bundle. 562 # Set of capabilities we can use to build the bundle.
560 if bundlecaps is None: 563 if bundlecaps is None:
561 bundlecaps = set() 564 bundlecaps = set()
562 self._bundlecaps = bundlecaps 565 self._bundlecaps = bundlecaps
566 self._isshallow = shallow
563 567
564 # experimental config: bundle.reorder 568 # experimental config: bundle.reorder
565 reorder = repo.ui.config('bundle', 'reorder') 569 reorder = repo.ui.config('bundle', 'reorder')
566 if reorder == 'auto': 570 if reorder == 'auto':
567 self._reorder = allowreorder 571 self._reorder = allowreorder
734 # parents) 738 # parents)
735 self._next_clrev_to_localrev.setdefault(cl.rev(x), 739 self._next_clrev_to_localrev.setdefault(cl.rev(x),
736 mfrevlog.rev(n)) 740 mfrevlog.rev(n))
737 # We can't trust the changed files list in the changeset if the 741 # We can't trust the changed files list in the changeset if the
738 # client requested a shallow clone. 742 # client requested a shallow clone.
739 if self._is_shallow: 743 if self._isshallow:
740 changedfiles.update(mfl[c[0]].read().keys()) 744 changedfiles.update(mfl[c[0]].read().keys())
741 else: 745 else:
742 changedfiles.update(c[3]) 746 changedfiles.update(c[3])
743 else: 747 else:
744 748
784 fastpathlinkrev, mfs, fnodes, source): 788 fastpathlinkrev, mfs, fnodes, source):
785 yield chunk 789 yield chunk
786 790
787 if ellipsesmode: 791 if ellipsesmode:
788 mfdicts = None 792 mfdicts = None
789 if self._is_shallow: 793 if self._isshallow:
790 mfdicts = [(self._repo.manifestlog[n].read(), lr) 794 mfdicts = [(self._repo.manifestlog[n].read(), lr)
791 for (n, lr) in mfs.iteritems()] 795 for (n, lr) in mfs.iteritems()]
792 796
793 mfs.clear() 797 mfs.clear()
794 clrevs = set(cl.rev(x) for x in clnodes) 798 clrevs = set(cl.rev(x) for x in clnodes)
890 894
891 # The 'source' parameter is useful for extensions 895 # The 'source' parameter is useful for extensions
892 def generatefiles(self, changedfiles, linknodes, commonrevs, source): 896 def generatefiles(self, changedfiles, linknodes, commonrevs, source):
893 changedfiles = list(filter(self._filematcher, changedfiles)) 897 changedfiles = list(filter(self._filematcher, changedfiles))
894 898
895 if getattr(self, '_is_shallow', False): 899 if self._isshallow:
896 # See comment in generate() for why this sadness is a thing. 900 # See comment in generate() for why this sadness is a thing.
897 mfdicts = self._mfdicts 901 mfdicts = self._mfdicts
898 del self._mfdicts 902 del self._mfdicts
899 # In a shallow clone, the linknodes callback needs to also include 903 # In a shallow clone, the linknodes callback needs to also include
900 # those file nodes that are in the manifests we sent but weren't 904 # those file nodes that are in the manifests we sent but weren't
1169 linknode=linknode, 1173 linknode=linknode,
1170 flags=flags, 1174 flags=flags,
1171 deltachunks=(diffheader, data), 1175 deltachunks=(diffheader, data),
1172 ) 1176 )
1173 1177
1174 def _makecg1packer(repo, filematcher, bundlecaps): 1178 def _makecg1packer(repo, filematcher, bundlecaps, shallow=False):
1175 builddeltaheader = lambda d: _CHANGEGROUPV1_DELTA_HEADER.pack( 1179 builddeltaheader = lambda d: _CHANGEGROUPV1_DELTA_HEADER.pack(
1176 d.node, d.p1node, d.p2node, d.linknode) 1180 d.node, d.p1node, d.p2node, d.linknode)
1177 1181
1178 return cgpacker(repo, filematcher, b'01', 1182 return cgpacker(repo, filematcher, b'01',
1179 useprevdelta=True, 1183 useprevdelta=True,
1180 allowreorder=None, 1184 allowreorder=None,
1181 builddeltaheader=builddeltaheader, 1185 builddeltaheader=builddeltaheader,
1182 manifestsend=b'', 1186 manifestsend=b'',
1183 sendtreemanifests=False, 1187 sendtreemanifests=False,
1184 bundlecaps=bundlecaps) 1188 bundlecaps=bundlecaps,
1185 1189 shallow=shallow)
1186 def _makecg2packer(repo, filematcher, bundlecaps): 1190
1191 def _makecg2packer(repo, filematcher, bundlecaps, shallow=False):
1187 builddeltaheader = lambda d: _CHANGEGROUPV2_DELTA_HEADER.pack( 1192 builddeltaheader = lambda d: _CHANGEGROUPV2_DELTA_HEADER.pack(
1188 d.node, d.p1node, d.p2node, d.basenode, d.linknode) 1193 d.node, d.p1node, d.p2node, d.basenode, d.linknode)
1189 1194
1190 # Since generaldelta is directly supported by cg2, reordering 1195 # Since generaldelta is directly supported by cg2, reordering
1191 # generally doesn't help, so we disable it by default (treating 1196 # generally doesn't help, so we disable it by default (treating
1194 useprevdelta=False, 1199 useprevdelta=False,
1195 allowreorder=False, 1200 allowreorder=False,
1196 builddeltaheader=builddeltaheader, 1201 builddeltaheader=builddeltaheader,
1197 manifestsend=b'', 1202 manifestsend=b'',
1198 sendtreemanifests=False, 1203 sendtreemanifests=False,
1199 bundlecaps=bundlecaps) 1204 bundlecaps=bundlecaps,
1200 1205 shallow=shallow)
1201 def _makecg3packer(repo, filematcher, bundlecaps): 1206
1207 def _makecg3packer(repo, filematcher, bundlecaps, shallow=False):
1202 builddeltaheader = lambda d: _CHANGEGROUPV3_DELTA_HEADER.pack( 1208 builddeltaheader = lambda d: _CHANGEGROUPV3_DELTA_HEADER.pack(
1203 d.node, d.p1node, d.p2node, d.basenode, d.linknode, d.flags) 1209 d.node, d.p1node, d.p2node, d.basenode, d.linknode, d.flags)
1204 1210
1205 return cgpacker(repo, filematcher, b'03', 1211 return cgpacker(repo, filematcher, b'03',
1206 useprevdelta=False, 1212 useprevdelta=False,
1207 allowreorder=False, 1213 allowreorder=False,
1208 builddeltaheader=builddeltaheader, 1214 builddeltaheader=builddeltaheader,
1209 manifestsend=closechunk(), 1215 manifestsend=closechunk(),
1210 sendtreemanifests=True, 1216 sendtreemanifests=True,
1211 bundlecaps=bundlecaps) 1217 bundlecaps=bundlecaps,
1218 shallow=shallow)
1212 1219
1213 _packermap = {'01': (_makecg1packer, cg1unpacker), 1220 _packermap = {'01': (_makecg1packer, cg1unpacker),
1214 # cg2 adds support for exchanging generaldelta 1221 # cg2 adds support for exchanging generaldelta
1215 '02': (_makecg2packer, cg2unpacker), 1222 '02': (_makecg2packer, cg2unpacker),
1216 # cg3 adds support for exchanging revlog flags and treemanifests 1223 # cg3 adds support for exchanging revlog flags and treemanifests
1266 if 'generaldelta' in repo.requirements: 1273 if 'generaldelta' in repo.requirements:
1267 versions.discard('01') 1274 versions.discard('01')
1268 assert versions 1275 assert versions
1269 return min(versions) 1276 return min(versions)
1270 1277
1271 def getbundler(version, repo, bundlecaps=None, filematcher=None): 1278 def getbundler(version, repo, bundlecaps=None, filematcher=None,
1279 shallow=False):
1272 assert version in supportedoutgoingversions(repo) 1280 assert version in supportedoutgoingversions(repo)
1273 1281
1274 if filematcher is None: 1282 if filematcher is None:
1275 filematcher = matchmod.alwaysmatcher(repo.root, '') 1283 filematcher = matchmod.alwaysmatcher(repo.root, '')
1276 1284
1282 # filter those out. 1290 # filter those out.
1283 filematcher = matchmod.intersectmatchers(repo.narrowmatch(), 1291 filematcher = matchmod.intersectmatchers(repo.narrowmatch(),
1284 filematcher) 1292 filematcher)
1285 1293
1286 fn = _packermap[version][0] 1294 fn = _packermap[version][0]
1287 return fn(repo, filematcher, bundlecaps) 1295 return fn(repo, filematcher, bundlecaps, shallow=shallow)
1288 1296
1289 def getunbundler(version, fh, alg, extras=None): 1297 def getunbundler(version, fh, alg, extras=None):
1290 return _packermap[version][1](fh, alg, extras=extras) 1298 return _packermap[version][1](fh, alg, extras=extras)
1291 1299
1292 def _changegroupinfo(repo, nodes, source): 1300 def _changegroupinfo(repo, nodes, source):
1377 # We wrap cg1packer.revchunk, using a side channel to pass 1385 # We wrap cg1packer.revchunk, using a side channel to pass
1378 # relevant_nodes into that area. Then if linknode isn't in the 1386 # relevant_nodes into that area. Then if linknode isn't in the
1379 # set, we know we have an ellipsis node and we should defer 1387 # set, we know we have an ellipsis node and we should defer
1380 # sending that node's data. We override close() to detect 1388 # sending that node's data. We override close() to detect
1381 # pending ellipsis nodes and flush them. 1389 # pending ellipsis nodes and flush them.
1382 packer = getbundler(version, repo, filematcher=match) 1390 packer = getbundler(version, repo, filematcher=match,
1391 shallow=depth is not None)
1383 # Give the packer the list of nodes which should not be 1392 # Give the packer the list of nodes which should not be
1384 # ellipsis nodes. We store this rather than the set of nodes 1393 # ellipsis nodes. We store this rather than the set of nodes
1385 # that should be an ellipsis because for very large histories 1394 # that should be an ellipsis because for very large histories
1386 # we expect this to be significantly smaller. 1395 # we expect this to be significantly smaller.
1387 packer._full_nodes = relevant_nodes 1396 packer._full_nodes = relevant_nodes
1393 packer._next_clrev_to_localrev = {} 1402 packer._next_clrev_to_localrev = {}
1394 # Maps changelog nodes to changelog revs. Filled in once 1403 # Maps changelog nodes to changelog revs. Filled in once
1395 # during changelog stage and then left unmodified. 1404 # during changelog stage and then left unmodified.
1396 packer._clnode_to_rev = {} 1405 packer._clnode_to_rev = {}
1397 packer._changelog_done = False 1406 packer._changelog_done = False
1398 # If true, informs the packer that it is serving shallow content and might
1399 # need to pack file contents not introduced by the changes being packed.
1400 packer._is_shallow = depth is not None
1401 1407
1402 return packer.generate(common, visitnodes, False, source) 1408 return packer.generate(common, visitnodes, False, source)