Mercurial > hg
view hgext/narrow/narrowchangegroup.py @ 38889:a06aab274aef
changegroup: move generatefiles() from narrow
The code is a bit ugly in that it overrides the linknodes
function that is passed in as a function. I'd like to think
that the caller of generatefiles() would pass in the appropriate
function. We can clean this up later.
Differential Revision: https://phab.mercurial-scm.org/D4066
author | Gregory Szorc <gregory.szorc@gmail.com> |
---|---|
date | Thu, 02 Aug 2018 12:18:35 -0700 |
parents | c9315bc578bc |
children |
line wrap: on
line source
# narrowchangegroup.py - narrow clone changegroup creation and consumption # # Copyright 2017 Google, Inc. # # This software may be used and distributed according to the terms of the # GNU General Public License version 2 or any later version. from __future__ import absolute_import from mercurial.i18n import _ from mercurial import ( changegroup, extensions, node, util, ) def setup(): def generate(orig, self, commonrevs, clnodes, fastpathlinkrev, source): '''yield a sequence of changegroup chunks (strings)''' # Note: other than delegating to orig, the only deviation in # logic from normal hg's generate is marked with BEGIN/END # NARROW HACK. if not util.safehasattr(self, 'full_nodes'): # not sending a narrow bundle for x in orig(self, commonrevs, clnodes, fastpathlinkrev, source): yield x return repo = self._repo cl = repo.changelog mfl = repo.manifestlog mfrevlog = mfl._revlog clrevorder = {} mfs = {} # needed manifests fnodes = {} # needed file nodes changedfiles = set() # Callback for the changelog, used to collect changed files and manifest # nodes. # Returns the linkrev node (identity in the changelog case). def lookupcl(x): c = cl.read(x) clrevorder[x] = len(clrevorder) # BEGIN NARROW HACK # # Only update mfs if x is going to be sent. Otherwise we # end up with bogus linkrevs specified for manifests and # we skip some manifest nodes that we should otherwise # have sent. if x in self.full_nodes or cl.rev(x) in self.precomputed_ellipsis: n = c[0] # record the first changeset introducing this manifest version mfs.setdefault(n, x) # Set this narrow-specific dict so we have the lowest manifest # revnum to look up for this cl revnum. (Part of mapping # changelog ellipsis parents to manifest ellipsis parents) self.next_clrev_to_localrev.setdefault(cl.rev(x), mfrevlog.rev(n)) # We can't trust the changed files list in the changeset if the # client requested a shallow clone. if self.is_shallow: changedfiles.update(mfl[c[0]].read().keys()) else: changedfiles.update(c[3]) # END NARROW HACK # Record a complete list of potentially-changed files in # this manifest. return x self._verbosenote(_('uncompressed size of bundle content:\n')) size = 0 for chunk in self.group(clnodes, cl, lookupcl, units=_('changesets')): size += len(chunk) yield chunk self._verbosenote(_('%8.i (changelog)\n') % size) # We need to make sure that the linkrev in the changegroup refers to # the first changeset that introduced the manifest or file revision. # The fastpath is usually safer than the slowpath, because the filelogs # are walked in revlog order. # # When taking the slowpath with reorder=None and the manifest revlog # uses generaldelta, the manifest may be walked in the "wrong" order. # Without 'clrevorder', we would get an incorrect linkrev (see fix in # cc0ff93d0c0c). # # When taking the fastpath, we are only vulnerable to reordering # of the changelog itself. The changelog never uses generaldelta, so # it is only reordered when reorder=True. To handle this case, we # simply take the slowpath, which already has the 'clrevorder' logic. # This was also fixed in cc0ff93d0c0c. fastpathlinkrev = fastpathlinkrev and not self._reorder # Treemanifests don't work correctly with fastpathlinkrev # either, because we don't discover which directory nodes to # send along with files. This could probably be fixed. fastpathlinkrev = fastpathlinkrev and ( 'treemanifest' not in repo.requirements) # Shallow clones also don't work correctly with fastpathlinkrev # because file nodes may need to be sent for a manifest even if they # weren't introduced by that manifest. fastpathlinkrev = fastpathlinkrev and not self.is_shallow for chunk in self.generatemanifests(commonrevs, clrevorder, fastpathlinkrev, mfs, fnodes, source): yield chunk # BEGIN NARROW HACK mfdicts = None if self.is_shallow: mfdicts = [(self._repo.manifestlog[n].read(), lr) for (n, lr) in mfs.iteritems()] # END NARROW HACK mfs.clear() clrevs = set(cl.rev(x) for x in clnodes) if not fastpathlinkrev: def linknodes(unused, fname): return fnodes.get(fname, {}) else: cln = cl.node def linknodes(filerevlog, fname): llr = filerevlog.linkrev fln = filerevlog.node revs = ((r, llr(r)) for r in filerevlog) return dict((fln(r), cln(lr)) for r, lr in revs if lr in clrevs) # BEGIN NARROW HACK # # We need to pass the mfdicts variable down into # generatefiles(), but more than one command might have # wrapped generatefiles so we can't modify the function # signature. Instead, we pass the data to ourselves using an # instance attribute. I'm sorry. self._mfdicts = mfdicts # END NARROW HACK for chunk in self.generatefiles(changedfiles, linknodes, commonrevs, source): yield chunk yield self.close() if clnodes: repo.hook('outgoing', node=node.hex(clnodes[0]), source=source) extensions.wrapfunction(changegroup.cg1packer, 'generate', generate)