diff --git a/hgext/narrow/__init__.py b/hgext/narrow/__init__.py --- a/hgext/narrow/__init__.py +++ b/hgext/narrow/__init__.py @@ -24,7 +24,6 @@ from . import ( narrowbundle2, - narrowchangegroup, narrowcommands, narrowcopies, narrowpatch, @@ -62,7 +61,6 @@ narrowrevlog.setup() narrowbundle2.setup() narrowcommands.setup() - narrowchangegroup.setup() narrowwirepeer.uisetup() def reposetup(ui, repo): diff --git a/hgext/narrow/narrowchangegroup.py b/hgext/narrow/narrowchangegroup.py deleted file mode 100644 --- a/hgext/narrow/narrowchangegroup.py +++ /dev/null @@ -1,145 +0,0 @@ -# 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) diff --git a/mercurial/changegroup.py b/mercurial/changegroup.py --- a/mercurial/changegroup.py +++ b/mercurial/changegroup.py @@ -657,20 +657,52 @@ clrevorder = {} mfs = {} # needed manifests fnodes = {} # needed file nodes + mfl = repo.manifestlog + # TODO violates storage abstraction. + mfrevlog = mfl._revlog changedfiles = set() - # Callback for the changelog, used to collect changed files and manifest - # nodes. + ellipsesmode = util.safehasattr(self, 'full_nodes') + + # 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) - n = c[0] - # record the first changeset introducing this manifest version - mfs.setdefault(n, x) - # Record a complete list of potentially-changed files in - # this manifest. - changedfiles.update(c[3]) + + if ellipsesmode: + # 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]) + else: + + n = c[0] + # record the first changeset introducing this manifest version + mfs.setdefault(n, x) + # Record a complete list of potentially-changed files in + # this manifest. + changedfiles.update(c[3]) + return x self._verbosenote(_('uncompressed size of bundle content:\n')) @@ -705,6 +737,13 @@ for chunk in self.generatemanifests(commonrevs, clrevorder, fastpathlinkrev, mfs, fnodes, source): yield chunk + + if ellipsesmode: + mfdicts = None + if self.is_shallow: + mfdicts = [(self._repo.manifestlog[n].read(), lr) + for (n, lr) in mfs.iteritems()] + mfs.clear() clrevs = set(cl.rev(x) for x in clnodes) @@ -719,6 +758,14 @@ revs = ((r, llr(r)) for r in filerevlog) return dict((fln(r), cln(lr)) for r, lr in revs if lr in clrevs) + if ellipsesmode: + # 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 + for chunk in self.generatefiles(changedfiles, linknodes, commonrevs, source): yield chunk