comparison mercurial/changegroup.py @ 38891:205c98e2f1ba

changegroup: rename "revlog" variables "revlog" shadows the module import. But more importantly, changegroup generation should be storage agnostic and not assume the existence of revlogs. Let's rename the thing providing revision storage to "store" to reflect this ideal property. Differential Revision: https://phab.mercurial-scm.org/D4073
author Gregory Szorc <gregory.szorc@gmail.com>
date Thu, 02 Aug 2018 17:05:11 -0700
parents d706c77449f9
children eb022ce9e505
comparison
equal deleted inserted replaced
38890:d706c77449f9 38891:205c98e2f1ba
555 555
556 def fileheader(self, fname): 556 def fileheader(self, fname):
557 return chunkheader(len(fname)) + fname 557 return chunkheader(len(fname)) + fname
558 558
559 # Extracted both for clarity and for overriding in extensions. 559 # Extracted both for clarity and for overriding in extensions.
560 def _sortgroup(self, revlog, nodelist, lookup): 560 def _sortgroup(self, store, nodelist, lookup):
561 """Sort nodes for change group and turn them into revnums.""" 561 """Sort nodes for change group and turn them into revnums."""
562 # Ellipses serving mode. 562 # Ellipses serving mode.
563 # 563 #
564 # In a perfect world, we'd generate better ellipsis-ified graphs 564 # In a perfect world, we'd generate better ellipsis-ified graphs
565 # for non-changelog revlogs. In practice, we haven't started doing 565 # for non-changelog revlogs. In practice, we haven't started doing
575 # order that they're introduced in dramatis personae by the 575 # order that they're introduced in dramatis personae by the
576 # changelog, so what we do is we sort the non-changelog histories 576 # changelog, so what we do is we sort the non-changelog histories
577 # by the order in which they are used by the changelog. 577 # by the order in which they are used by the changelog.
578 if util.safehasattr(self, 'full_nodes') and self.clnode_to_rev: 578 if util.safehasattr(self, 'full_nodes') and self.clnode_to_rev:
579 key = lambda n: self.clnode_to_rev[lookup(n)] 579 key = lambda n: self.clnode_to_rev[lookup(n)]
580 return [revlog.rev(n) for n in sorted(nodelist, key=key)] 580 return [store.rev(n) for n in sorted(nodelist, key=key)]
581 581
582 # for generaldelta revlogs, we linearize the revs; this will both be 582 # for generaldelta revlogs, we linearize the revs; this will both be
583 # much quicker and generate a much smaller bundle 583 # much quicker and generate a much smaller bundle
584 if (revlog._generaldelta and self._reorder is None) or self._reorder: 584 if (store._generaldelta and self._reorder is None) or self._reorder:
585 dag = dagutil.revlogdag(revlog) 585 dag = dagutil.revlogdag(store)
586 return dag.linearize(set(revlog.rev(n) for n in nodelist)) 586 return dag.linearize(set(store.rev(n) for n in nodelist))
587 else: 587 else:
588 return sorted([revlog.rev(n) for n in nodelist]) 588 return sorted([store.rev(n) for n in nodelist])
589 589
590 def group(self, nodelist, revlog, lookup, units=None): 590 def group(self, nodelist, store, lookup, units=None):
591 """Calculate a delta group, yielding a sequence of changegroup chunks 591 """Calculate a delta group, yielding a sequence of changegroup chunks
592 (strings). 592 (strings).
593 593
594 Given a list of changeset revs, return a set of deltas and 594 Given a list of changeset revs, return a set of deltas and
595 metadata corresponding to nodes. The first delta is 595 metadata corresponding to nodes. The first delta is
604 # if we don't have any revisions touched by these changesets, bail 604 # if we don't have any revisions touched by these changesets, bail
605 if len(nodelist) == 0: 605 if len(nodelist) == 0:
606 yield self.close() 606 yield self.close()
607 return 607 return
608 608
609 revs = self._sortgroup(revlog, nodelist, lookup) 609 revs = self._sortgroup(store, nodelist, lookup)
610 610
611 # add the parent of the first rev 611 # add the parent of the first rev
612 p = revlog.parentrevs(revs[0])[0] 612 p = store.parentrevs(revs[0])[0]
613 revs.insert(0, p) 613 revs.insert(0, p)
614 614
615 # build deltas 615 # build deltas
616 progress = None 616 progress = None
617 if units is not None: 617 if units is not None:
619 total=(len(revs) - 1)) 619 total=(len(revs) - 1))
620 for r in pycompat.xrange(len(revs) - 1): 620 for r in pycompat.xrange(len(revs) - 1):
621 if progress: 621 if progress:
622 progress.update(r + 1) 622 progress.update(r + 1)
623 prev, curr = revs[r], revs[r + 1] 623 prev, curr = revs[r], revs[r + 1]
624 linknode = lookup(revlog.node(curr)) 624 linknode = lookup(store.node(curr))
625 for c in self.revchunk(revlog, curr, prev, linknode): 625 for c in self.revchunk(store, curr, prev, linknode):
626 yield c 626 yield c
627 627
628 if progress: 628 if progress:
629 progress.complete() 629 progress.complete()
630 yield self.close() 630 yield self.close()
631 631
632 # filter any nodes that claim to be part of the known set 632 # filter any nodes that claim to be part of the known set
633 def prune(self, revlog, missing, commonrevs): 633 def prune(self, store, missing, commonrevs):
634 # TODO this violates storage abstraction for manifests. 634 # TODO this violates storage abstraction for manifests.
635 if isinstance(revlog, manifest.manifestrevlog): 635 if isinstance(store, manifest.manifestrevlog):
636 if not self._filematcher.visitdir(revlog._dir[:-1] or '.'): 636 if not self._filematcher.visitdir(store._dir[:-1] or '.'):
637 return [] 637 return []
638 638
639 rr, rl = revlog.rev, revlog.linkrev 639 rr, rl = store.rev, store.linkrev
640 return [n for n in missing if rl(rr(n)) not in commonrevs] 640 return [n for n in missing if rl(rr(n)) not in commonrevs]
641 641
642 def _packmanifests(self, dir, mfnodes, lookuplinknode): 642 def _packmanifests(self, dir, mfnodes, lookuplinknode):
643 """Pack flat manifests into a changegroup stream.""" 643 """Pack flat manifests into a changegroup stream."""
644 assert not dir 644 assert not dir
904 size += len(chunk) 904 size += len(chunk)
905 yield chunk 905 yield chunk
906 self._verbosenote(_('%8.i %s\n') % (size, fname)) 906 self._verbosenote(_('%8.i %s\n') % (size, fname))
907 progress.complete() 907 progress.complete()
908 908
909 def deltaparent(self, revlog, rev, p1, p2, prev): 909 def deltaparent(self, store, rev, p1, p2, prev):
910 if not revlog.candelta(prev, rev): 910 if not store.candelta(prev, rev):
911 raise error.ProgrammingError('cg1 should not be used in this case') 911 raise error.ProgrammingError('cg1 should not be used in this case')
912 return prev 912 return prev
913 913
914 def revchunk(self, revlog, rev, prev, linknode): 914 def revchunk(self, store, rev, prev, linknode):
915 if util.safehasattr(self, 'full_nodes'): 915 if util.safehasattr(self, 'full_nodes'):
916 fn = self._revchunknarrow 916 fn = self._revchunknarrow
917 else: 917 else:
918 fn = self._revchunknormal 918 fn = self._revchunknormal
919 919
920 return fn(revlog, rev, prev, linknode) 920 return fn(store, rev, prev, linknode)
921 921
922 def _revchunknormal(self, revlog, rev, prev, linknode): 922 def _revchunknormal(self, store, rev, prev, linknode):
923 node = revlog.node(rev) 923 node = store.node(rev)
924 p1, p2 = revlog.parentrevs(rev) 924 p1, p2 = store.parentrevs(rev)
925 base = self.deltaparent(revlog, rev, p1, p2, prev) 925 base = self.deltaparent(store, rev, p1, p2, prev)
926 926
927 prefix = '' 927 prefix = ''
928 if revlog.iscensored(base) or revlog.iscensored(rev): 928 if store.iscensored(base) or store.iscensored(rev):
929 try: 929 try:
930 delta = revlog.revision(node, raw=True) 930 delta = store.revision(node, raw=True)
931 except error.CensoredNodeError as e: 931 except error.CensoredNodeError as e:
932 delta = e.tombstone 932 delta = e.tombstone
933 if base == nullrev: 933 if base == nullrev:
934 prefix = mdiff.trivialdiffheader(len(delta)) 934 prefix = mdiff.trivialdiffheader(len(delta))
935 else: 935 else:
936 baselen = revlog.rawsize(base) 936 baselen = store.rawsize(base)
937 prefix = mdiff.replacediffheader(baselen, len(delta)) 937 prefix = mdiff.replacediffheader(baselen, len(delta))
938 elif base == nullrev: 938 elif base == nullrev:
939 delta = revlog.revision(node, raw=True) 939 delta = store.revision(node, raw=True)
940 prefix = mdiff.trivialdiffheader(len(delta)) 940 prefix = mdiff.trivialdiffheader(len(delta))
941 else: 941 else:
942 delta = revlog.revdiff(base, rev) 942 delta = store.revdiff(base, rev)
943 p1n, p2n = revlog.parents(node) 943 p1n, p2n = store.parents(node)
944 basenode = revlog.node(base) 944 basenode = store.node(base)
945 flags = revlog.flags(rev) 945 flags = store.flags(rev)
946 meta = self.builddeltaheader(node, p1n, p2n, basenode, linknode, flags) 946 meta = self.builddeltaheader(node, p1n, p2n, basenode, linknode, flags)
947 meta += prefix 947 meta += prefix
948 l = len(meta) + len(delta) 948 l = len(meta) + len(delta)
949 yield chunkheader(l) 949 yield chunkheader(l)
950 yield meta 950 yield meta
951 yield delta 951 yield delta
952 952
953 def _revchunknarrow(self, revlog, rev, prev, linknode): 953 def _revchunknarrow(self, store, rev, prev, linknode):
954 # build up some mapping information that's useful later. See 954 # build up some mapping information that's useful later. See
955 # the local() nested function below. 955 # the local() nested function below.
956 if not self.changelog_done: 956 if not self.changelog_done:
957 self.clnode_to_rev[linknode] = rev 957 self.clnode_to_rev[linknode] = rev
958 linkrev = rev 958 linkrev = rev
962 self.clrev_to_localrev[linkrev] = rev 962 self.clrev_to_localrev[linkrev] = rev
963 963
964 # This is a node to send in full, because the changeset it 964 # This is a node to send in full, because the changeset it
965 # corresponds to was a full changeset. 965 # corresponds to was a full changeset.
966 if linknode in self.full_nodes: 966 if linknode in self.full_nodes:
967 for x in self._revchunknormal(revlog, rev, prev, linknode): 967 for x in self._revchunknormal(store, rev, prev, linknode):
968 yield x 968 yield x
969 return 969 return
970 970
971 # At this point, a node can either be one we should skip or an 971 # At this point, a node can either be one we should skip or an
972 # ellipsis. If it's not an ellipsis, bail immediately. 972 # ellipsis. If it's not an ellipsis, bail immediately.
992 # have a parent that is already on the client, and we 992 # have a parent that is already on the client, and we
993 # need to store some extra mapping information so that 993 # need to store some extra mapping information so that
994 # our contained ellipsis nodes will be able to resolve 994 # our contained ellipsis nodes will be able to resolve
995 # their parents. 995 # their parents.
996 if clrev not in self.clrev_to_localrev: 996 if clrev not in self.clrev_to_localrev:
997 clnode = revlog.node(clrev) 997 clnode = store.node(clrev)
998 self.clnode_to_rev[clnode] = clrev 998 self.clnode_to_rev[clnode] = clrev
999 return clrev 999 return clrev
1000 1000
1001 # Walk the ellipsis-ized changelog breadth-first looking for a 1001 # Walk the ellipsis-ized changelog breadth-first looking for a
1002 # change that has been linked from the current revlog. 1002 # change that has been linked from the current revlog.
1032 # thorough) cases in our testsuite, but I would be 1032 # thorough) cases in our testsuite, but I would be
1033 # somewhat unsurprised to find a case in the wild 1033 # somewhat unsurprised to find a case in the wild
1034 # where this breaks down a bit. That said, I don't 1034 # where this breaks down a bit. That said, I don't
1035 # know if it would hurt anything. 1035 # know if it would hurt anything.
1036 for i in pycompat.xrange(rev, 0, -1): 1036 for i in pycompat.xrange(rev, 0, -1):
1037 if revlog.linkrev(i) == clrev: 1037 if store.linkrev(i) == clrev:
1038 return i 1038 return i
1039 # We failed to resolve a parent for this node, so 1039 # We failed to resolve a parent for this node, so
1040 # we crash the changegroup construction. 1040 # we crash the changegroup construction.
1041 raise error.Abort( 1041 raise error.Abort(
1042 'unable to resolve parent while packing %r %r' 1042 'unable to resolve parent while packing %r %r'
1043 ' for changeset %r' % (revlog.indexfile, rev, clrev)) 1043 ' for changeset %r' % (store.indexfile, rev, clrev))
1044 1044
1045 return nullrev 1045 return nullrev
1046 1046
1047 if not linkparents or ( 1047 if not linkparents or (
1048 revlog.parentrevs(rev) == (nullrev, nullrev)): 1048 store.parentrevs(rev) == (nullrev, nullrev)):
1049 p1, p2 = nullrev, nullrev 1049 p1, p2 = nullrev, nullrev
1050 elif len(linkparents) == 1: 1050 elif len(linkparents) == 1:
1051 p1, = sorted(local(p) for p in linkparents) 1051 p1, = sorted(local(p) for p in linkparents)
1052 p2 = nullrev 1052 p2 = nullrev
1053 else: 1053 else:
1054 p1, p2 = sorted(local(p) for p in linkparents) 1054 p1, p2 = sorted(local(p) for p in linkparents)
1055 n = revlog.node(rev) 1055 n = store.node(rev)
1056 1056
1057 yield ellipsisdata( 1057 yield ellipsisdata(
1058 self, rev, revlog, p1, p2, revlog.revision(n), linknode) 1058 self, rev, store, p1, p2, store.revision(n), linknode)
1059 1059
1060 def builddeltaheader(self, node, p1n, p2n, basenode, linknode, flags): 1060 def builddeltaheader(self, node, p1n, p2n, basenode, linknode, flags):
1061 # do nothing with basenode, it is implicitly the previous one in HG10 1061 # do nothing with basenode, it is implicitly the previous one in HG10
1062 # do nothing with flags, it is implicitly 0 for cg1 and cg2 1062 # do nothing with flags, it is implicitly 0 for cg1 and cg2
1063 return struct.pack(self.deltaheader, node, p1n, p2n, linknode) 1063 return struct.pack(self.deltaheader, node, p1n, p2n, linknode)
1074 # Since generaldelta is directly supported by cg2, reordering 1074 # Since generaldelta is directly supported by cg2, reordering
1075 # generally doesn't help, so we disable it by default (treating 1075 # generally doesn't help, so we disable it by default (treating
1076 # bundle.reorder=auto just like bundle.reorder=False). 1076 # bundle.reorder=auto just like bundle.reorder=False).
1077 self._reorder = False 1077 self._reorder = False
1078 1078
1079 def deltaparent(self, revlog, rev, p1, p2, prev): 1079 def deltaparent(self, store, rev, p1, p2, prev):
1080 # Narrow ellipses mode. 1080 # Narrow ellipses mode.
1081 if util.safehasattr(self, 'full_nodes'): 1081 if util.safehasattr(self, 'full_nodes'):
1082 # TODO: send better deltas when in narrow mode. 1082 # TODO: send better deltas when in narrow mode.
1083 # 1083 #
1084 # changegroup.group() loops over revisions to send, 1084 # changegroup.group() loops over revisions to send,
1090 # significantly smarter about delta bases. This is 1090 # significantly smarter about delta bases. This is
1091 # slightly tricky because this same code has to work for 1091 # slightly tricky because this same code has to work for
1092 # all revlogs, and we don't have the linkrev/linknode here. 1092 # all revlogs, and we don't have the linkrev/linknode here.
1093 return p1 1093 return p1
1094 1094
1095 dp = revlog.deltaparent(rev) 1095 dp = store.deltaparent(rev)
1096 if dp == nullrev and revlog.storedeltachains: 1096 if dp == nullrev and store.storedeltachains:
1097 # Avoid sending full revisions when delta parent is null. Pick prev 1097 # Avoid sending full revisions when delta parent is null. Pick prev
1098 # in that case. It's tempting to pick p1 in this case, as p1 will 1098 # in that case. It's tempting to pick p1 in this case, as p1 will
1099 # be smaller in the common case. However, computing a delta against 1099 # be smaller in the common case. However, computing a delta against
1100 # p1 may require resolving the raw text of p1, which could be 1100 # p1 may require resolving the raw text of p1, which could be
1101 # expensive. The revlog caches should have prev cached, meaning 1101 # expensive. The revlog caches should have prev cached, meaning
1109 elif dp not in (p1, p2, prev): 1109 elif dp not in (p1, p2, prev):
1110 # Pick prev when we can't be sure remote has the base revision. 1110 # Pick prev when we can't be sure remote has the base revision.
1111 return prev 1111 return prev
1112 else: 1112 else:
1113 base = dp 1113 base = dp
1114 if base != nullrev and not revlog.candelta(base, rev): 1114 if base != nullrev and not store.candelta(base, rev):
1115 base = nullrev 1115 base = nullrev
1116 return base 1116 return base
1117 1117
1118 def builddeltaheader(self, node, p1n, p2n, basenode, linknode, flags): 1118 def builddeltaheader(self, node, p1n, p2n, basenode, linknode, flags):
1119 # Do nothing with flags, it is implicitly 0 in cg1 and cg2 1119 # Do nothing with flags, it is implicitly 0 in cg1 and cg2