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 |