comparison mercurial/changegroup.py @ 38903:68e490ed640e

changegroup: make some packer attributes private These methods and attributes are low level and should not be called or outside outside of instances. Indicate as such through naming. Differential Revision: https://phab.mercurial-scm.org/D4085
author Gregory Szorc <gregory.szorc@gmail.com>
date Fri, 03 Aug 2018 12:47:15 -0700
parents 4c99c6d1ef95
children cdb9bc216771
comparison
equal deleted inserted replaced
38902:4c99c6d1ef95 38903:68e490ed640e
573 if self._repo.ui.verbose and not self._repo.ui.debugflag: 573 if self._repo.ui.verbose and not self._repo.ui.debugflag:
574 self._verbosenote = self._repo.ui.note 574 self._verbosenote = self._repo.ui.note
575 else: 575 else:
576 self._verbosenote = lambda s: None 576 self._verbosenote = lambda s: None
577 577
578 def close(self): 578 def _close(self):
579 # Ellipses serving mode. 579 # Ellipses serving mode.
580 getattr(self, 'clrev_to_localrev', {}).clear() 580 getattr(self, '_clrev_to_localrev', {}).clear()
581 if getattr(self, 'next_clrev_to_localrev', {}): 581 if getattr(self, '_next_clrev_to_localrev', {}):
582 self.clrev_to_localrev = self.next_clrev_to_localrev 582 self._clrev_to_localrev = self._next_clrev_to_localrev
583 del self.next_clrev_to_localrev 583 del self._next_clrev_to_localrev
584 self.changelog_done = True 584 self._changelog_done = True
585 585
586 return closechunk() 586 return closechunk()
587 587
588 def fileheader(self, fname): 588 def _fileheader(self, fname):
589 return chunkheader(len(fname)) + fname 589 return chunkheader(len(fname)) + fname
590 590
591 # Extracted both for clarity and for overriding in extensions. 591 # Extracted both for clarity and for overriding in extensions.
592 def _sortgroup(self, store, nodelist, lookup): 592 def _sortgroup(self, store, nodelist, lookup):
593 """Sort nodes for change group and turn them into revnums.""" 593 """Sort nodes for change group and turn them into revnums."""
605 # The one invariant we *know* holds is that the new (potentially 605 # The one invariant we *know* holds is that the new (potentially
606 # bogus) DAG shape will be valid if we order the nodes in the 606 # bogus) DAG shape will be valid if we order the nodes in the
607 # order that they're introduced in dramatis personae by the 607 # order that they're introduced in dramatis personae by the
608 # changelog, so what we do is we sort the non-changelog histories 608 # changelog, so what we do is we sort the non-changelog histories
609 # by the order in which they are used by the changelog. 609 # by the order in which they are used by the changelog.
610 if util.safehasattr(self, 'full_nodes') and self.clnode_to_rev: 610 if util.safehasattr(self, '_full_nodes') and self._clnode_to_rev:
611 key = lambda n: self.clnode_to_rev[lookup(n)] 611 key = lambda n: self._clnode_to_rev[lookup(n)]
612 return [store.rev(n) for n in sorted(nodelist, key=key)] 612 return [store.rev(n) for n in sorted(nodelist, key=key)]
613 613
614 # for generaldelta revlogs, we linearize the revs; this will both be 614 # for generaldelta revlogs, we linearize the revs; this will both be
615 # much quicker and generate a much smaller bundle 615 # much quicker and generate a much smaller bundle
616 if (store._generaldelta and self._reorder is None) or self._reorder: 616 if (store._generaldelta and self._reorder is None) or self._reorder:
633 If units is not None, progress detail will be generated, units specifies 633 If units is not None, progress detail will be generated, units specifies
634 the type of revlog that is touched (changelog, manifest, etc.). 634 the type of revlog that is touched (changelog, manifest, etc.).
635 """ 635 """
636 # if we don't have any revisions touched by these changesets, bail 636 # if we don't have any revisions touched by these changesets, bail
637 if len(nodelist) == 0: 637 if len(nodelist) == 0:
638 yield self.close() 638 yield self._close()
639 return 639 return
640 640
641 revs = self._sortgroup(store, nodelist, lookup) 641 revs = self._sortgroup(store, nodelist, lookup)
642 642
643 # add the parent of the first rev 643 # add the parent of the first rev
652 for r in pycompat.xrange(len(revs) - 1): 652 for r in pycompat.xrange(len(revs) - 1):
653 if progress: 653 if progress:
654 progress.update(r + 1) 654 progress.update(r + 1)
655 prev, curr = revs[r], revs[r + 1] 655 prev, curr = revs[r], revs[r + 1]
656 linknode = lookup(store.node(curr)) 656 linknode = lookup(store.node(curr))
657 for c in self.revchunk(store, curr, prev, linknode): 657 for c in self._revchunk(store, curr, prev, linknode):
658 yield c 658 yield c
659 659
660 if progress: 660 if progress:
661 progress.complete() 661 progress.complete()
662 yield self.close() 662 yield self._close()
663 663
664 # filter any nodes that claim to be part of the known set 664 # filter any nodes that claim to be part of the known set
665 def prune(self, store, missing, commonrevs): 665 def _prune(self, store, missing, commonrevs):
666 # TODO this violates storage abstraction for manifests. 666 # TODO this violates storage abstraction for manifests.
667 if isinstance(store, manifest.manifestrevlog): 667 if isinstance(store, manifest.manifestrevlog):
668 if not self._filematcher.visitdir(store._dir[:-1] or '.'): 668 if not self._filematcher.visitdir(store._dir[:-1] or '.'):
669 return [] 669 return []
670 670
685 can be sent. 685 can be sent.
686 """ 686 """
687 assert self.version == b'03' 687 assert self.version == b'03'
688 688
689 if dir: 689 if dir:
690 yield self.fileheader(dir) 690 yield self._fileheader(dir)
691 691
692 # TODO violates storage abstractions by assuming revlogs. 692 # TODO violates storage abstractions by assuming revlogs.
693 dirlog = self._repo.manifestlog._revlog.dirlog(dir) 693 dirlog = self._repo.manifestlog._revlog.dirlog(dir)
694 for chunk in self.group(mfnodes, dirlog, lookuplinknode, 694 for chunk in self.group(mfnodes, dirlog, lookuplinknode,
695 units=_('manifests')): 695 units=_('manifests')):
706 mfl = repo.manifestlog 706 mfl = repo.manifestlog
707 # TODO violates storage abstraction. 707 # TODO violates storage abstraction.
708 mfrevlog = mfl._revlog 708 mfrevlog = mfl._revlog
709 changedfiles = set() 709 changedfiles = set()
710 710
711 ellipsesmode = util.safehasattr(self, 'full_nodes') 711 ellipsesmode = util.safehasattr(self, '_full_nodes')
712 712
713 # Callback for the changelog, used to collect changed files and 713 # Callback for the changelog, used to collect changed files and
714 # manifest nodes. 714 # manifest nodes.
715 # Returns the linkrev node (identity in the changelog case). 715 # Returns the linkrev node (identity in the changelog case).
716 def lookupcl(x): 716 def lookupcl(x):
720 if ellipsesmode: 720 if ellipsesmode:
721 # Only update mfs if x is going to be sent. Otherwise we 721 # Only update mfs if x is going to be sent. Otherwise we
722 # end up with bogus linkrevs specified for manifests and 722 # end up with bogus linkrevs specified for manifests and
723 # we skip some manifest nodes that we should otherwise 723 # we skip some manifest nodes that we should otherwise
724 # have sent. 724 # have sent.
725 if (x in self.full_nodes 725 if (x in self._full_nodes
726 or cl.rev(x) in self.precomputed_ellipsis): 726 or cl.rev(x) in self._precomputed_ellipsis):
727 n = c[0] 727 n = c[0]
728 # Record the first changeset introducing this manifest 728 # Record the first changeset introducing this manifest
729 # version. 729 # version.
730 mfs.setdefault(n, x) 730 mfs.setdefault(n, x)
731 # Set this narrow-specific dict so we have the lowest 731 # Set this narrow-specific dict so we have the lowest
732 # manifest revnum to look up for this cl revnum. (Part of 732 # manifest revnum to look up for this cl revnum. (Part of
733 # mapping changelog ellipsis parents to manifest ellipsis 733 # mapping changelog ellipsis parents to manifest ellipsis
734 # parents) 734 # parents)
735 self.next_clrev_to_localrev.setdefault(cl.rev(x), 735 self._next_clrev_to_localrev.setdefault(cl.rev(x),
736 mfrevlog.rev(n)) 736 mfrevlog.rev(n))
737 # We can't trust the changed files list in the changeset if the 737 # We can't trust the changed files list in the changeset if the
738 # client requested a shallow clone. 738 # client requested a shallow clone.
739 if self.is_shallow: 739 if self._is_shallow:
740 changedfiles.update(mfl[c[0]].read().keys()) 740 changedfiles.update(mfl[c[0]].read().keys())
741 else: 741 else:
742 changedfiles.update(c[3]) 742 changedfiles.update(c[3])
743 else: 743 else:
744 744
784 fastpathlinkrev, mfs, fnodes, source): 784 fastpathlinkrev, mfs, fnodes, source):
785 yield chunk 785 yield chunk
786 786
787 if ellipsesmode: 787 if ellipsesmode:
788 mfdicts = None 788 mfdicts = None
789 if self.is_shallow: 789 if self._is_shallow:
790 mfdicts = [(self._repo.manifestlog[n].read(), lr) 790 mfdicts = [(self._repo.manifestlog[n].read(), lr)
791 for (n, lr) in mfs.iteritems()] 791 for (n, lr) in mfs.iteritems()]
792 792
793 mfs.clear() 793 mfs.clear()
794 clrevs = set(cl.rev(x) for x in clnodes) 794 clrevs = set(cl.rev(x) for x in clnodes)
814 814
815 for chunk in self.generatefiles(changedfiles, linknodes, commonrevs, 815 for chunk in self.generatefiles(changedfiles, linknodes, commonrevs,
816 source): 816 source):
817 yield chunk 817 yield chunk
818 818
819 yield self.close() 819 yield self._close()
820 820
821 if clnodes: 821 if clnodes:
822 repo.hook('outgoing', node=hex(clnodes[0]), source=source) 822 repo.hook('outgoing', node=hex(clnodes[0]), source=source)
823 823
824 def generatemanifests(self, commonrevs, clrevorder, fastpathlinkrev, mfs, 824 def generatemanifests(self, commonrevs, clrevorder, fastpathlinkrev, mfs,
878 fn = (self._packtreemanifests if self._sendtreemanifests 878 fn = (self._packtreemanifests if self._sendtreemanifests
879 else self._packmanifests) 879 else self._packmanifests)
880 size = 0 880 size = 0
881 while tmfnodes: 881 while tmfnodes:
882 dir, nodes = tmfnodes.popitem() 882 dir, nodes = tmfnodes.popitem()
883 prunednodes = self.prune(dirlog(dir), nodes, commonrevs) 883 prunednodes = self._prune(dirlog(dir), nodes, commonrevs)
884 if not dir or prunednodes: 884 if not dir or prunednodes:
885 for x in fn(dir, prunednodes, makelookupmflinknode(dir, nodes)): 885 for x in fn(dir, prunednodes, makelookupmflinknode(dir, nodes)):
886 size += len(x) 886 size += len(x)
887 yield x 887 yield x
888 self._verbosenote(_('%8.i (manifests)\n') % size) 888 self._verbosenote(_('%8.i (manifests)\n') % size)
890 890
891 # The 'source' parameter is useful for extensions 891 # The 'source' parameter is useful for extensions
892 def generatefiles(self, changedfiles, linknodes, commonrevs, source): 892 def generatefiles(self, changedfiles, linknodes, commonrevs, source):
893 changedfiles = list(filter(self._filematcher, changedfiles)) 893 changedfiles = list(filter(self._filematcher, changedfiles))
894 894
895 if getattr(self, 'is_shallow', False): 895 if getattr(self, '_is_shallow', False):
896 # See comment in generate() for why this sadness is a thing. 896 # See comment in generate() for why this sadness is a thing.
897 mfdicts = self._mfdicts 897 mfdicts = self._mfdicts
898 del self._mfdicts 898 del self._mfdicts
899 # In a shallow clone, the linknodes callback needs to also include 899 # In a shallow clone, the linknodes callback needs to also include
900 # those file nodes that are in the manifests we sent but weren't 900 # those file nodes that are in the manifests we sent but weren't
908 # TODO have caller pass in appropriate function. 908 # TODO have caller pass in appropriate function.
909 def linknodes(flog, fname): 909 def linknodes(flog, fname):
910 for c in commonctxs: 910 for c in commonctxs:
911 try: 911 try:
912 fnode = c.filenode(fname) 912 fnode = c.filenode(fname)
913 self.clrev_to_localrev[c.rev()] = flog.rev(fnode) 913 self._clrev_to_localrev[c.rev()] = flog.rev(fnode)
914 except error.ManifestLookupError: 914 except error.ManifestLookupError:
915 pass 915 pass
916 links = oldlinknodes(flog, fname) 916 links = oldlinknodes(flog, fname)
917 if len(links) != len(mfdicts): 917 if len(links) != len(mfdicts):
918 for mf, lr in mfdicts: 918 for mf, lr in mfdicts:
939 # Lookup for filenodes, we collected the linkrev nodes above in the 939 # Lookup for filenodes, we collected the linkrev nodes above in the
940 # fastpath case and with lookupmf in the slowpath case. 940 # fastpath case and with lookupmf in the slowpath case.
941 def lookupfilelog(x): 941 def lookupfilelog(x):
942 return linkrevnodes[x] 942 return linkrevnodes[x]
943 943
944 filenodes = self.prune(filerevlog, linkrevnodes, commonrevs) 944 filenodes = self._prune(filerevlog, linkrevnodes, commonrevs)
945 if filenodes: 945 if filenodes:
946 progress.update(i + 1, item=fname) 946 progress.update(i + 1, item=fname)
947 h = self.fileheader(fname) 947 h = self._fileheader(fname)
948 size = len(h) 948 size = len(h)
949 yield h 949 yield h
950 for chunk in self.group(filenodes, filerevlog, lookupfilelog): 950 for chunk in self.group(filenodes, filerevlog, lookupfilelog):
951 size += len(chunk) 951 size += len(chunk)
952 yield chunk 952 yield chunk
953 self._verbosenote(_('%8.i %s\n') % (size, fname)) 953 self._verbosenote(_('%8.i %s\n') % (size, fname))
954 progress.complete() 954 progress.complete()
955 955
956 def deltaparent(self, store, rev, p1, p2, prev): 956 def _deltaparent(self, store, rev, p1, p2, prev):
957 if self._useprevdelta: 957 if self._useprevdelta:
958 if not store.candelta(prev, rev): 958 if not store.candelta(prev, rev):
959 raise error.ProgrammingError( 959 raise error.ProgrammingError(
960 'cg1 should not be used in this case') 960 'cg1 should not be used in this case')
961 return prev 961 return prev
962 962
963 # Narrow ellipses mode. 963 # Narrow ellipses mode.
964 if util.safehasattr(self, 'full_nodes'): 964 if util.safehasattr(self, '_full_nodes'):
965 # TODO: send better deltas when in narrow mode. 965 # TODO: send better deltas when in narrow mode.
966 # 966 #
967 # changegroup.group() loops over revisions to send, 967 # changegroup.group() loops over revisions to send,
968 # including revisions we'll skip. What this means is that 968 # including revisions we'll skip. What this means is that
969 # `prev` will be a potentially useless delta base for all 969 # `prev` will be a potentially useless delta base for all
998 if base != nullrev and not store.candelta(base, rev): 998 if base != nullrev and not store.candelta(base, rev):
999 base = nullrev 999 base = nullrev
1000 1000
1001 return base 1001 return base
1002 1002
1003 def revchunk(self, store, rev, prev, linknode): 1003 def _revchunk(self, store, rev, prev, linknode):
1004 if util.safehasattr(self, 'full_nodes'): 1004 if util.safehasattr(self, '_full_nodes'):
1005 fn = self._revisiondeltanarrow 1005 fn = self._revisiondeltanarrow
1006 else: 1006 else:
1007 fn = self._revisiondeltanormal 1007 fn = self._revisiondeltanormal
1008 1008
1009 delta = fn(store, rev, prev, linknode) 1009 delta = fn(store, rev, prev, linknode)
1019 yield x 1019 yield x
1020 1020
1021 def _revisiondeltanormal(self, store, rev, prev, linknode): 1021 def _revisiondeltanormal(self, store, rev, prev, linknode):
1022 node = store.node(rev) 1022 node = store.node(rev)
1023 p1, p2 = store.parentrevs(rev) 1023 p1, p2 = store.parentrevs(rev)
1024 base = self.deltaparent(store, rev, p1, p2, prev) 1024 base = self._deltaparent(store, rev, p1, p2, prev)
1025 1025
1026 prefix = '' 1026 prefix = ''
1027 if store.iscensored(base) or store.iscensored(rev): 1027 if store.iscensored(base) or store.iscensored(rev):
1028 try: 1028 try:
1029 delta = store.revision(node, raw=True) 1029 delta = store.revision(node, raw=True)
1052 ) 1052 )
1053 1053
1054 def _revisiondeltanarrow(self, store, rev, prev, linknode): 1054 def _revisiondeltanarrow(self, store, rev, prev, linknode):
1055 # build up some mapping information that's useful later. See 1055 # build up some mapping information that's useful later. See
1056 # the local() nested function below. 1056 # the local() nested function below.
1057 if not self.changelog_done: 1057 if not self._changelog_done:
1058 self.clnode_to_rev[linknode] = rev 1058 self._clnode_to_rev[linknode] = rev
1059 linkrev = rev 1059 linkrev = rev
1060 self.clrev_to_localrev[linkrev] = rev 1060 self._clrev_to_localrev[linkrev] = rev
1061 else: 1061 else:
1062 linkrev = self.clnode_to_rev[linknode] 1062 linkrev = self._clnode_to_rev[linknode]
1063 self.clrev_to_localrev[linkrev] = rev 1063 self._clrev_to_localrev[linkrev] = rev
1064 1064
1065 # This is a node to send in full, because the changeset it 1065 # This is a node to send in full, because the changeset it
1066 # corresponds to was a full changeset. 1066 # corresponds to was a full changeset.
1067 if linknode in self.full_nodes: 1067 if linknode in self._full_nodes:
1068 return self._revisiondeltanormal(store, rev, prev, linknode) 1068 return self._revisiondeltanormal(store, rev, prev, linknode)
1069 1069
1070 # At this point, a node can either be one we should skip or an 1070 # At this point, a node can either be one we should skip or an
1071 # ellipsis. If it's not an ellipsis, bail immediately. 1071 # ellipsis. If it's not an ellipsis, bail immediately.
1072 if linkrev not in self.precomputed_ellipsis: 1072 if linkrev not in self._precomputed_ellipsis:
1073 return 1073 return
1074 1074
1075 linkparents = self.precomputed_ellipsis[linkrev] 1075 linkparents = self._precomputed_ellipsis[linkrev]
1076 def local(clrev): 1076 def local(clrev):
1077 """Turn a changelog revnum into a local revnum. 1077 """Turn a changelog revnum into a local revnum.
1078 1078
1079 The ellipsis dag is stored as revnums on the changelog, 1079 The ellipsis dag is stored as revnums on the changelog,
1080 but when we're producing ellipsis entries for 1080 but when we're producing ellipsis entries for
1084 mappings as needed. 1084 mappings as needed.
1085 """ 1085 """
1086 if clrev == nullrev: 1086 if clrev == nullrev:
1087 return nullrev 1087 return nullrev
1088 1088
1089 if not self.changelog_done: 1089 if not self._changelog_done:
1090 # If we're doing the changelog, it's possible that we 1090 # If we're doing the changelog, it's possible that we
1091 # have a parent that is already on the client, and we 1091 # have a parent that is already on the client, and we
1092 # need to store some extra mapping information so that 1092 # need to store some extra mapping information so that
1093 # our contained ellipsis nodes will be able to resolve 1093 # our contained ellipsis nodes will be able to resolve
1094 # their parents. 1094 # their parents.
1095 if clrev not in self.clrev_to_localrev: 1095 if clrev not in self._clrev_to_localrev:
1096 clnode = store.node(clrev) 1096 clnode = store.node(clrev)
1097 self.clnode_to_rev[clnode] = clrev 1097 self._clnode_to_rev[clnode] = clrev
1098 return clrev 1098 return clrev
1099 1099
1100 # Walk the ellipsis-ized changelog breadth-first looking for a 1100 # Walk the ellipsis-ized changelog breadth-first looking for a
1101 # change that has been linked from the current revlog. 1101 # change that has been linked from the current revlog.
1102 # 1102 #
1109 # nodes even after ellipsis-izing. 1109 # nodes even after ellipsis-izing.
1110 walk = [clrev] 1110 walk = [clrev]
1111 while walk: 1111 while walk:
1112 p = walk[0] 1112 p = walk[0]
1113 walk = walk[1:] 1113 walk = walk[1:]
1114 if p in self.clrev_to_localrev: 1114 if p in self._clrev_to_localrev:
1115 return self.clrev_to_localrev[p] 1115 return self._clrev_to_localrev[p]
1116 elif p in self.full_nodes: 1116 elif p in self._full_nodes:
1117 walk.extend([pp for pp in self._repo.changelog.parentrevs(p) 1117 walk.extend([pp for pp in self._repo.changelog.parentrevs(p)
1118 if pp != nullrev]) 1118 if pp != nullrev])
1119 elif p in self.precomputed_ellipsis: 1119 elif p in self._precomputed_ellipsis:
1120 walk.extend([pp for pp in self.precomputed_ellipsis[p] 1120 walk.extend([pp for pp in self._precomputed_ellipsis[p]
1121 if pp != nullrev]) 1121 if pp != nullrev])
1122 else: 1122 else:
1123 # In this case, we've got an ellipsis with parents 1123 # In this case, we've got an ellipsis with parents
1124 # outside the current bundle (likely an 1124 # outside the current bundle (likely an
1125 # incremental pull). We "know" that we can use the 1125 # incremental pull). We "know" that we can use the
1382 packer = getbundler(version, repo, filematcher=match) 1382 packer = getbundler(version, repo, filematcher=match)
1383 # Give the packer the list of nodes which should not be 1383 # Give the packer the list of nodes which should not be
1384 # ellipsis nodes. We store this rather than the set of nodes 1384 # ellipsis nodes. We store this rather than the set of nodes
1385 # that should be an ellipsis because for very large histories 1385 # that should be an ellipsis because for very large histories
1386 # we expect this to be significantly smaller. 1386 # we expect this to be significantly smaller.
1387 packer.full_nodes = relevant_nodes 1387 packer._full_nodes = relevant_nodes
1388 # Maps ellipsis revs to their roots at the changelog level. 1388 # Maps ellipsis revs to their roots at the changelog level.
1389 packer.precomputed_ellipsis = ellipsisroots 1389 packer._precomputed_ellipsis = ellipsisroots
1390 # Maps CL revs to per-revlog revisions. Cleared in close() at 1390 # Maps CL revs to per-revlog revisions. Cleared in close() at
1391 # the end of each group. 1391 # the end of each group.
1392 packer.clrev_to_localrev = {} 1392 packer._clrev_to_localrev = {}
1393 packer.next_clrev_to_localrev = {} 1393 packer._next_clrev_to_localrev = {}
1394 # Maps changelog nodes to changelog revs. Filled in once 1394 # Maps changelog nodes to changelog revs. Filled in once
1395 # during changelog stage and then left unmodified. 1395 # during changelog stage and then left unmodified.
1396 packer.clnode_to_rev = {} 1396 packer._clnode_to_rev = {}
1397 packer.changelog_done = False 1397 packer._changelog_done = False
1398 # If true, informs the packer that it is serving shallow content and might 1398 # If true, informs the packer that it is serving shallow content and might
1399 # need to pack file contents not introduced by the changes being packed. 1399 # need to pack file contents not introduced by the changes being packed.
1400 packer.is_shallow = depth is not None 1400 packer._is_shallow = depth is not None
1401 1401
1402 return packer.generate(common, visitnodes, False, source) 1402 return packer.generate(common, visitnodes, False, source)