Mercurial > hg
comparison mercurial/changegroup.py @ 39001:a6e1ff40e335
changegroup: pass clrevtolocalrev to each group
clrevtolocalrev is a per-changegroup group mapping revisions to
aid with shallow clone.
Back when this functionality was implemented in an extension, this
dict was added to the packer instance so monkeypatched functions
could reference it there. Now that this code is part of core, we
can pass the dict to each consumer properly so it doesn't have to
live on the cgpacker instance. This commit does that.
Differential Revision: https://phab.mercurial-scm.org/D4193
author | Gregory Szorc <gregory.szorc@gmail.com> |
---|---|
date | Tue, 07 Aug 2018 14:52:00 -0700 |
parents | 4cff37564006 |
children | eb8a0139ace3 |
comparison
equal
deleted
inserted
replaced
39000:4cff37564006 | 39001:a6e1ff40e335 |
---|---|
656 if self._repo.ui.verbose and not self._repo.ui.debugflag: | 656 if self._repo.ui.verbose and not self._repo.ui.debugflag: |
657 self._verbosenote = self._repo.ui.note | 657 self._verbosenote = self._repo.ui.note |
658 else: | 658 else: |
659 self._verbosenote = lambda s: None | 659 self._verbosenote = lambda s: None |
660 | 660 |
661 # Maps CL revs to per-revlog revisions. Cleared in close() at | |
662 # the end of each group. | |
663 self._clrevtolocalrev = {} | |
664 | |
665 def _close(self): | 661 def _close(self): |
666 # Ellipses serving mode. | |
667 self._clrevtolocalrev.clear() | |
668 | |
669 return closechunk() | 662 return closechunk() |
670 | 663 |
671 def group(self, revs, store, ischangelog, lookup, units=None): | 664 def group(self, revs, store, ischangelog, lookup, units=None, |
665 clrevtolocalrev=None): | |
672 """Calculate a delta group, yielding a sequence of changegroup chunks | 666 """Calculate a delta group, yielding a sequence of changegroup chunks |
673 (strings). | 667 (strings). |
674 | 668 |
675 Given a list of changeset revs, return a set of deltas and | 669 Given a list of changeset revs, return a set of deltas and |
676 metadata corresponding to nodes. The first delta is | 670 metadata corresponding to nodes. The first delta is |
704 prev, curr = revs[r], revs[r + 1] | 698 prev, curr = revs[r], revs[r + 1] |
705 linknode = lookup(store.node(curr)) | 699 linknode = lookup(store.node(curr)) |
706 | 700 |
707 if self._ellipses: | 701 if self._ellipses: |
708 linkrev = cl.rev(linknode) | 702 linkrev = cl.rev(linknode) |
709 self._clrevtolocalrev[linkrev] = curr | 703 clrevtolocalrev[linkrev] = curr |
710 | 704 |
711 # This is a node to send in full, because the changeset it | 705 # This is a node to send in full, because the changeset it |
712 # corresponds to was a full changeset. | 706 # corresponds to was a full changeset. |
713 if linknode in self._fullclnodes: | 707 if linknode in self._fullclnodes: |
714 delta = _revisiondeltanormal(store, curr, prev, linknode, | 708 delta = _revisiondeltanormal(store, curr, prev, linknode, |
715 self._deltaparentfn) | 709 self._deltaparentfn) |
716 elif linkrev not in self._precomputedellipsis: | 710 elif linkrev not in self._precomputedellipsis: |
717 delta = None | 711 delta = None |
718 else: | 712 else: |
719 delta = self._revisiondeltanarrow(store, ischangelog, | 713 delta = self._revisiondeltanarrow(store, ischangelog, |
720 curr, linkrev, linknode) | 714 curr, linkrev, linknode, |
715 clrevtolocalrev) | |
721 else: | 716 else: |
722 delta = _revisiondeltanormal(store, curr, prev, linknode, | 717 delta = _revisiondeltanormal(store, curr, prev, linknode, |
723 self._deltaparentfn) | 718 self._deltaparentfn) |
724 | 719 |
725 if not delta: | 720 if not delta: |
744 return [] | 739 return [] |
745 | 740 |
746 rr, rl = store.rev, store.linkrev | 741 rr, rl = store.rev, store.linkrev |
747 return [n for n in missing if rl(rr(n)) not in commonrevs] | 742 return [n for n in missing if rl(rr(n)) not in commonrevs] |
748 | 743 |
749 def _packmanifests(self, dir, dirlog, revs, lookuplinknode): | 744 def _packmanifests(self, dir, dirlog, revs, lookuplinknode, |
745 clrevtolocalrev): | |
750 """Pack manifests into a changegroup stream. | 746 """Pack manifests into a changegroup stream. |
751 | 747 |
752 Encodes the directory name in the output so multiple manifests | 748 Encodes the directory name in the output so multiple manifests |
753 can be sent. Multiple manifests is not supported by cg1 and cg2. | 749 can be sent. Multiple manifests is not supported by cg1 and cg2. |
754 """ | 750 """ |
755 if dir: | 751 if dir: |
756 assert self.version == b'03' | 752 assert self.version == b'03' |
757 yield _fileheader(dir) | 753 yield _fileheader(dir) |
758 | 754 |
759 for chunk in self.group(revs, dirlog, False, lookuplinknode, | 755 for chunk in self.group(revs, dirlog, False, lookuplinknode, |
760 units=_('manifests')): | 756 units=_('manifests'), |
757 clrevtolocalrev=clrevtolocalrev): | |
761 yield chunk | 758 yield chunk |
762 | 759 |
763 def generate(self, commonrevs, clnodes, fastpathlinkrev, source): | 760 def generate(self, commonrevs, clnodes, fastpathlinkrev, source): |
764 """Yield a sequence of changegroup byte chunks.""" | 761 """Yield a sequence of changegroup byte chunks.""" |
765 | 762 |
777 self._verbosenote(_('%8.i (changelog)\n') % size) | 774 self._verbosenote(_('%8.i (changelog)\n') % size) |
778 | 775 |
779 clrevorder = clstate['clrevorder'] | 776 clrevorder = clstate['clrevorder'] |
780 mfs = clstate['mfs'] | 777 mfs = clstate['mfs'] |
781 changedfiles = clstate['changedfiles'] | 778 changedfiles = clstate['changedfiles'] |
782 | |
783 if self._ellipses: | |
784 self._clrevtolocalrev = clstate['clrevtomanifestrev'] | |
785 | 779 |
786 # We need to make sure that the linkrev in the changegroup refers to | 780 # We need to make sure that the linkrev in the changegroup refers to |
787 # the first changeset that introduced the manifest or file revision. | 781 # the first changeset that introduced the manifest or file revision. |
788 # The fastpath is usually safer than the slowpath, because the filelogs | 782 # The fastpath is usually safer than the slowpath, because the filelogs |
789 # are walked in revlog order. | 783 # are walked in revlog order. |
806 'treemanifest' not in repo.requirements) | 800 'treemanifest' not in repo.requirements) |
807 | 801 |
808 fnodes = {} # needed file nodes | 802 fnodes = {} # needed file nodes |
809 | 803 |
810 for chunk in self.generatemanifests(commonrevs, clrevorder, | 804 for chunk in self.generatemanifests(commonrevs, clrevorder, |
811 fastpathlinkrev, mfs, fnodes, source): | 805 fastpathlinkrev, mfs, fnodes, source, |
806 clstate['clrevtomanifestrev']): | |
812 yield chunk | 807 yield chunk |
813 | 808 |
814 mfdicts = None | 809 mfdicts = None |
815 if self._ellipses and self._isshallow: | 810 if self._ellipses and self._isshallow: |
816 mfdicts = [(self._repo.manifestlog[n].read(), lr) | 811 mfdicts = [(self._repo.manifestlog[n].read(), lr) |
893 'mfs': mfs, | 888 'mfs': mfs, |
894 'changedfiles': changedfiles, | 889 'changedfiles': changedfiles, |
895 'clrevtomanifestrev': clrevtomanifestrev, | 890 'clrevtomanifestrev': clrevtomanifestrev, |
896 } | 891 } |
897 | 892 |
898 gen = self.group(revs, cl, True, lookupcl, units=_('changesets')) | 893 gen = self.group(revs, cl, True, lookupcl, units=_('changesets'), |
894 clrevtolocalrev={}) | |
899 | 895 |
900 return state, gen | 896 return state, gen |
901 | 897 |
902 def generatemanifests(self, commonrevs, clrevorder, fastpathlinkrev, mfs, | 898 def generatemanifests(self, commonrevs, clrevorder, fastpathlinkrev, mfs, |
903 fnodes, source): | 899 fnodes, source, clrevtolocalrev): |
904 """Returns an iterator of changegroup chunks containing manifests. | 900 """Returns an iterator of changegroup chunks containing manifests. |
905 | 901 |
906 `source` is unused here, but is used by extensions like remotefilelog to | 902 `source` is unused here, but is used by extensions like remotefilelog to |
907 change what is sent based in pulls vs pushes, etc. | 903 change what is sent based in pulls vs pushes, etc. |
908 """ | 904 """ |
967 lookupfn) | 963 lookupfn) |
968 else: | 964 else: |
969 revs = _sortnodesnormal(store, prunednodes, | 965 revs = _sortnodesnormal(store, prunednodes, |
970 self._reorder) | 966 self._reorder) |
971 | 967 |
972 for x in self._packmanifests(dir, store, revs, lookupfn): | 968 for x in self._packmanifests(dir, store, revs, lookupfn, |
969 clrevtolocalrev): | |
973 size += len(x) | 970 size += len(x) |
974 yield x | 971 yield x |
975 self._verbosenote(_('%8.i (manifests)\n') % size) | 972 self._verbosenote(_('%8.i (manifests)\n') % size) |
976 yield self._manifestsend | 973 yield self._manifestsend |
977 | 974 |
990 flinkrev = store.linkrev | 987 flinkrev = store.linkrev |
991 fnode = store.node | 988 fnode = store.node |
992 revs = ((r, flinkrev(r)) for r in store) | 989 revs = ((r, flinkrev(r)) for r in store) |
993 return dict((fnode(r), cln(lr)) | 990 return dict((fnode(r), cln(lr)) |
994 for r, lr in revs if lr in clrevs) | 991 for r, lr in revs if lr in clrevs) |
992 | |
993 clrevtolocalrev = {} | |
995 | 994 |
996 if self._isshallow: | 995 if self._isshallow: |
997 # In a shallow clone, the linknodes callback needs to also include | 996 # In a shallow clone, the linknodes callback needs to also include |
998 # those file nodes that are in the manifests we sent but weren't | 997 # those file nodes that are in the manifests we sent but weren't |
999 # introduced by those manifests. | 998 # introduced by those manifests. |
1005 # TODO have caller pass in appropriate function. | 1004 # TODO have caller pass in appropriate function. |
1006 def linknodes(flog, fname): | 1005 def linknodes(flog, fname): |
1007 for c in commonctxs: | 1006 for c in commonctxs: |
1008 try: | 1007 try: |
1009 fnode = c.filenode(fname) | 1008 fnode = c.filenode(fname) |
1010 self._clrevtolocalrev[c.rev()] = flog.rev(fnode) | 1009 clrevtolocalrev[c.rev()] = flog.rev(fnode) |
1011 except error.ManifestLookupError: | 1010 except error.ManifestLookupError: |
1012 pass | 1011 pass |
1013 links = normallinknodes(flog, fname) | 1012 links = normallinknodes(flog, fname) |
1014 if len(links) != len(mfdicts): | 1013 if len(links) != len(mfdicts): |
1015 for mf, lr in mfdicts: | 1014 for mf, lr in mfdicts: |
1030 filerevlog = repo.file(fname) | 1029 filerevlog = repo.file(fname) |
1031 if not filerevlog: | 1030 if not filerevlog: |
1032 raise error.Abort(_("empty or missing file data for %s") % | 1031 raise error.Abort(_("empty or missing file data for %s") % |
1033 fname) | 1032 fname) |
1034 | 1033 |
1034 clrevtolocalrev.clear() | |
1035 | |
1035 linkrevnodes = linknodes(filerevlog, fname) | 1036 linkrevnodes = linknodes(filerevlog, fname) |
1036 # Lookup for filenodes, we collected the linkrev nodes above in the | 1037 # Lookup for filenodes, we collected the linkrev nodes above in the |
1037 # fastpath case and with lookupmf in the slowpath case. | 1038 # fastpath case and with lookupmf in the slowpath case. |
1038 def lookupfilelog(x): | 1039 def lookupfilelog(x): |
1039 return linkrevnodes[x] | 1040 return linkrevnodes[x] |
1049 | 1050 |
1050 progress.update(i + 1, item=fname) | 1051 progress.update(i + 1, item=fname) |
1051 h = _fileheader(fname) | 1052 h = _fileheader(fname) |
1052 size = len(h) | 1053 size = len(h) |
1053 yield h | 1054 yield h |
1054 for chunk in self.group(revs, filerevlog, False, lookupfilelog): | 1055 for chunk in self.group(revs, filerevlog, False, lookupfilelog, |
1056 clrevtolocalrev=clrevtolocalrev): | |
1055 size += len(chunk) | 1057 size += len(chunk) |
1056 yield chunk | 1058 yield chunk |
1057 self._verbosenote(_('%8.i %s\n') % (size, fname)) | 1059 self._verbosenote(_('%8.i %s\n') % (size, fname)) |
1058 progress.complete() | 1060 progress.complete() |
1059 | 1061 |
1060 def _revisiondeltanarrow(self, store, ischangelog, rev, linkrev, linknode): | 1062 def _revisiondeltanarrow(self, store, ischangelog, rev, linkrev, linknode, |
1063 clrevtolocalrev): | |
1061 linkparents = self._precomputedellipsis[linkrev] | 1064 linkparents = self._precomputedellipsis[linkrev] |
1062 def local(clrev): | 1065 def local(clrev): |
1063 """Turn a changelog revnum into a local revnum. | 1066 """Turn a changelog revnum into a local revnum. |
1064 | 1067 |
1065 The ellipsis dag is stored as revnums on the changelog, | 1068 The ellipsis dag is stored as revnums on the changelog, |
1087 # nodes even after ellipsis-izing. | 1090 # nodes even after ellipsis-izing. |
1088 walk = [clrev] | 1091 walk = [clrev] |
1089 while walk: | 1092 while walk: |
1090 p = walk[0] | 1093 p = walk[0] |
1091 walk = walk[1:] | 1094 walk = walk[1:] |
1092 if p in self._clrevtolocalrev: | 1095 if p in clrevtolocalrev: |
1093 return self._clrevtolocalrev[p] | 1096 return clrevtolocalrev[p] |
1094 elif p in self._fullclnodes: | 1097 elif p in self._fullclnodes: |
1095 walk.extend([pp for pp in self._repo.changelog.parentrevs(p) | 1098 walk.extend([pp for pp in self._repo.changelog.parentrevs(p) |
1096 if pp != nullrev]) | 1099 if pp != nullrev]) |
1097 elif p in self._precomputedellipsis: | 1100 elif p in self._precomputedellipsis: |
1098 walk.extend([pp for pp in self._precomputedellipsis[p] | 1101 walk.extend([pp for pp in self._precomputedellipsis[p] |