mercurial/changegroup.py
changeset 39014 f7228c907ef4
parent 39013 87b737b78bd0
child 39016 d2ab0db89465
equal deleted inserted replaced
39013:87b737b78bd0 39014:f7228c907ef4
   713         for chunk in self.group(mfnodes, dirlog, lookuplinknode,
   713         for chunk in self.group(mfnodes, dirlog, lookuplinknode,
   714                                 units=_('manifests')):
   714                                 units=_('manifests')):
   715             yield chunk
   715             yield chunk
   716 
   716 
   717     def generate(self, commonrevs, clnodes, fastpathlinkrev, source):
   717     def generate(self, commonrevs, clnodes, fastpathlinkrev, source):
   718         '''yield a sequence of changegroup chunks (strings)'''
   718         """Yield a sequence of changegroup byte chunks."""
       
   719 
   719         repo = self._repo
   720         repo = self._repo
   720         cl = repo.changelog
   721         cl = repo.changelog
   721 
   722 
       
   723         self._verbosenote(_('uncompressed size of bundle content:\n'))
       
   724         size = 0
       
   725 
       
   726         clstate, chunks = self._generatechangelog(cl, clnodes)
       
   727         for chunk in chunks:
       
   728             size += len(chunk)
       
   729             yield chunk
       
   730 
       
   731         self._verbosenote(_('%8.i (changelog)\n') % size)
       
   732 
       
   733         clrevorder = clstate['clrevorder']
       
   734         mfs = clstate['mfs']
       
   735         changedfiles = clstate['changedfiles']
       
   736 
       
   737         # We need to make sure that the linkrev in the changegroup refers to
       
   738         # the first changeset that introduced the manifest or file revision.
       
   739         # The fastpath is usually safer than the slowpath, because the filelogs
       
   740         # are walked in revlog order.
       
   741         #
       
   742         # When taking the slowpath with reorder=None and the manifest revlog
       
   743         # uses generaldelta, the manifest may be walked in the "wrong" order.
       
   744         # Without 'clrevorder', we would get an incorrect linkrev (see fix in
       
   745         # cc0ff93d0c0c).
       
   746         #
       
   747         # When taking the fastpath, we are only vulnerable to reordering
       
   748         # of the changelog itself. The changelog never uses generaldelta, so
       
   749         # it is only reordered when reorder=True. To handle this case, we
       
   750         # simply take the slowpath, which already has the 'clrevorder' logic.
       
   751         # This was also fixed in cc0ff93d0c0c.
       
   752         fastpathlinkrev = fastpathlinkrev and not self._reorder
       
   753         # Treemanifests don't work correctly with fastpathlinkrev
       
   754         # either, because we don't discover which directory nodes to
       
   755         # send along with files. This could probably be fixed.
       
   756         fastpathlinkrev = fastpathlinkrev and (
       
   757             'treemanifest' not in repo.requirements)
       
   758 
       
   759         fnodes = {}  # needed file nodes
       
   760 
       
   761         for chunk in self.generatemanifests(commonrevs, clrevorder,
       
   762                 fastpathlinkrev, mfs, fnodes, source):
       
   763             yield chunk
       
   764 
       
   765         if self._ellipses:
       
   766             mfdicts = None
       
   767             if self._isshallow:
       
   768                 mfdicts = [(self._repo.manifestlog[n].read(), lr)
       
   769                            for (n, lr) in mfs.iteritems()]
       
   770 
       
   771         mfs.clear()
       
   772         clrevs = set(cl.rev(x) for x in clnodes)
       
   773 
       
   774         if not fastpathlinkrev:
       
   775             def linknodes(unused, fname):
       
   776                 return fnodes.get(fname, {})
       
   777         else:
       
   778             cln = cl.node
       
   779             def linknodes(filerevlog, fname):
       
   780                 llr = filerevlog.linkrev
       
   781                 fln = filerevlog.node
       
   782                 revs = ((r, llr(r)) for r in filerevlog)
       
   783                 return dict((fln(r), cln(lr)) for r, lr in revs if lr in clrevs)
       
   784 
       
   785         if self._ellipses:
       
   786             # We need to pass the mfdicts variable down into
       
   787             # generatefiles(), but more than one command might have
       
   788             # wrapped generatefiles so we can't modify the function
       
   789             # signature. Instead, we pass the data to ourselves using an
       
   790             # instance attribute. I'm sorry.
       
   791             self._mfdicts = mfdicts
       
   792 
       
   793         for chunk in self.generatefiles(changedfiles, linknodes, commonrevs,
       
   794                                         source):
       
   795             yield chunk
       
   796 
       
   797         yield self._close()
       
   798 
       
   799         if clnodes:
       
   800             repo.hook('outgoing', node=hex(clnodes[0]), source=source)
       
   801 
       
   802     def _generatechangelog(self, cl, nodes):
       
   803         """Generate data for changelog chunks.
       
   804 
       
   805         Returns a 2-tuple of a dict containing state and an iterable of
       
   806         byte chunks. The state will not be fully populated until the
       
   807         chunk stream has been fully consumed.
       
   808         """
   722         clrevorder = {}
   809         clrevorder = {}
   723         mfs = {} # needed manifests
   810         mfs = {} # needed manifests
   724         fnodes = {} # needed file nodes
   811         mfl = self._repo.manifestlog
   725         mfl = repo.manifestlog
       
   726         # TODO violates storage abstraction.
   812         # TODO violates storage abstraction.
   727         mfrevlog = mfl._revlog
   813         mfrevlog = mfl._revlog
   728         changedfiles = set()
   814         changedfiles = set()
   729 
   815 
   730         # Callback for the changelog, used to collect changed files and
   816         # Callback for the changelog, used to collect changed files and
   766                 # this manifest.
   852                 # this manifest.
   767                 changedfiles.update(c[3])
   853                 changedfiles.update(c[3])
   768 
   854 
   769             return x
   855             return x
   770 
   856 
   771         self._verbosenote(_('uncompressed size of bundle content:\n'))
   857         state = {
   772         size = 0
   858             'clrevorder': clrevorder,
   773         for chunk in self.group(clnodes, cl, lookupcl, units=_('changesets')):
   859             'mfs': mfs,
   774             size += len(chunk)
   860             'changedfiles': changedfiles,
   775             yield chunk
   861         }
   776         self._verbosenote(_('%8.i (changelog)\n') % size)
   862 
   777 
   863         gen = self.group(nodes, cl, lookupcl, units=_('changesets'))
   778         # We need to make sure that the linkrev in the changegroup refers to
   864 
   779         # the first changeset that introduced the manifest or file revision.
   865         return state, gen
   780         # The fastpath is usually safer than the slowpath, because the filelogs
       
   781         # are walked in revlog order.
       
   782         #
       
   783         # When taking the slowpath with reorder=None and the manifest revlog
       
   784         # uses generaldelta, the manifest may be walked in the "wrong" order.
       
   785         # Without 'clrevorder', we would get an incorrect linkrev (see fix in
       
   786         # cc0ff93d0c0c).
       
   787         #
       
   788         # When taking the fastpath, we are only vulnerable to reordering
       
   789         # of the changelog itself. The changelog never uses generaldelta, so
       
   790         # it is only reordered when reorder=True. To handle this case, we
       
   791         # simply take the slowpath, which already has the 'clrevorder' logic.
       
   792         # This was also fixed in cc0ff93d0c0c.
       
   793         fastpathlinkrev = fastpathlinkrev and not self._reorder
       
   794         # Treemanifests don't work correctly with fastpathlinkrev
       
   795         # either, because we don't discover which directory nodes to
       
   796         # send along with files. This could probably be fixed.
       
   797         fastpathlinkrev = fastpathlinkrev and (
       
   798             'treemanifest' not in repo.requirements)
       
   799 
       
   800         for chunk in self.generatemanifests(commonrevs, clrevorder,
       
   801                 fastpathlinkrev, mfs, fnodes, source):
       
   802             yield chunk
       
   803 
       
   804         if self._ellipses:
       
   805             mfdicts = None
       
   806             if self._isshallow:
       
   807                 mfdicts = [(self._repo.manifestlog[n].read(), lr)
       
   808                            for (n, lr) in mfs.iteritems()]
       
   809 
       
   810         mfs.clear()
       
   811         clrevs = set(cl.rev(x) for x in clnodes)
       
   812 
       
   813         if not fastpathlinkrev:
       
   814             def linknodes(unused, fname):
       
   815                 return fnodes.get(fname, {})
       
   816         else:
       
   817             cln = cl.node
       
   818             def linknodes(filerevlog, fname):
       
   819                 llr = filerevlog.linkrev
       
   820                 fln = filerevlog.node
       
   821                 revs = ((r, llr(r)) for r in filerevlog)
       
   822                 return dict((fln(r), cln(lr)) for r, lr in revs if lr in clrevs)
       
   823 
       
   824         if self._ellipses:
       
   825             # We need to pass the mfdicts variable down into
       
   826             # generatefiles(), but more than one command might have
       
   827             # wrapped generatefiles so we can't modify the function
       
   828             # signature. Instead, we pass the data to ourselves using an
       
   829             # instance attribute. I'm sorry.
       
   830             self._mfdicts = mfdicts
       
   831 
       
   832         for chunk in self.generatefiles(changedfiles, linknodes, commonrevs,
       
   833                                         source):
       
   834             yield chunk
       
   835 
       
   836         yield self._close()
       
   837 
       
   838         if clnodes:
       
   839             repo.hook('outgoing', node=hex(clnodes[0]), source=source)
       
   840 
   866 
   841     def generatemanifests(self, commonrevs, clrevorder, fastpathlinkrev, mfs,
   867     def generatemanifests(self, commonrevs, clrevorder, fastpathlinkrev, mfs,
   842                           fnodes, source):
   868                           fnodes, source):
   843         """Returns an iterator of changegroup chunks containing manifests.
   869         """Returns an iterator of changegroup chunks containing manifests.
   844 
   870