comparison mercurial/changegroup.py @ 38901:23ae0c07a3e1

changegroup: control delta parent behavior via constructor The last remaining override on cg2packer related to parent delta computation. We pass a parameter to the constructor to control whether to delta against the previous revision and we inline all parent delta logic into a single function. With this change, cg2packer is empty, so it has been deleted. Differential Revision: https://phab.mercurial-scm.org/D4083
author Gregory Szorc <gregory.szorc@gmail.com>
date Fri, 03 Aug 2018 10:35:02 -0700
parents 6e999a2d8fe7
children 4c99c6d1ef95
comparison
equal deleted inserted replaced
38900:6e999a2d8fe7 38901:23ae0c07a3e1
519 # Iterable of chunks holding raw delta data. 519 # Iterable of chunks holding raw delta data.
520 deltachunks = attr.ib() 520 deltachunks = attr.ib()
521 521
522 class cg1packer(object): 522 class cg1packer(object):
523 def __init__(self, repo, filematcher, version, allowreorder, 523 def __init__(self, repo, filematcher, version, allowreorder,
524 builddeltaheader, manifestsend, sendtreemanifests, 524 useprevdelta, builddeltaheader, manifestsend,
525 bundlecaps=None): 525 sendtreemanifests, bundlecaps=None):
526 """Given a source repo, construct a bundler. 526 """Given a source repo, construct a bundler.
527 527
528 filematcher is a matcher that matches on files to include in the 528 filematcher is a matcher that matches on files to include in the
529 changegroup. Used to facilitate sparse changegroups. 529 changegroup. Used to facilitate sparse changegroups.
530 530
531 allowreorder controls whether reordering of revisions is allowed. 531 allowreorder controls whether reordering of revisions is allowed.
532 This value is used when ``bundle.reorder`` is ``auto`` or isn't 532 This value is used when ``bundle.reorder`` is ``auto`` or isn't
533 set. 533 set.
534
535 useprevdelta controls whether revisions should always delta against
536 the previous revision in the changegroup.
534 537
535 builddeltaheader is a callable that constructs the header for a group 538 builddeltaheader is a callable that constructs the header for a group
536 delta. 539 delta.
537 540
538 manifestsend is a chunk to send after manifests have been fully emitted. 541 manifestsend is a chunk to send after manifests have been fully emitted.
546 """ 549 """
547 assert filematcher 550 assert filematcher
548 self._filematcher = filematcher 551 self._filematcher = filematcher
549 552
550 self.version = version 553 self.version = version
554 self._useprevdelta = useprevdelta
551 self._builddeltaheader = builddeltaheader 555 self._builddeltaheader = builddeltaheader
552 self._manifestsend = manifestsend 556 self._manifestsend = manifestsend
553 self._sendtreemanifests = sendtreemanifests 557 self._sendtreemanifests = sendtreemanifests
554 558
555 # Set of capabilities we can use to build the bundle. 559 # Set of capabilities we can use to build the bundle.
948 yield chunk 952 yield chunk
949 self._verbosenote(_('%8.i %s\n') % (size, fname)) 953 self._verbosenote(_('%8.i %s\n') % (size, fname))
950 progress.complete() 954 progress.complete()
951 955
952 def deltaparent(self, store, rev, p1, p2, prev): 956 def deltaparent(self, store, rev, p1, p2, prev):
953 if not store.candelta(prev, rev): 957 if self._useprevdelta:
954 raise error.ProgrammingError('cg1 should not be used in this case') 958 if not store.candelta(prev, rev):
955 return prev 959 raise error.ProgrammingError(
960 'cg1 should not be used in this case')
961 return prev
962
963 # Narrow ellipses mode.
964 if util.safehasattr(self, 'full_nodes'):
965 # TODO: send better deltas when in narrow mode.
966 #
967 # changegroup.group() loops over revisions to send,
968 # including revisions we'll skip. What this means is that
969 # `prev` will be a potentially useless delta base for all
970 # ellipsis nodes, as the client likely won't have it. In
971 # the future we should do bookkeeping about which nodes
972 # have been sent to the client, and try to be
973 # significantly smarter about delta bases. This is
974 # slightly tricky because this same code has to work for
975 # all revlogs, and we don't have the linkrev/linknode here.
976 return p1
977
978 dp = store.deltaparent(rev)
979 if dp == nullrev and store.storedeltachains:
980 # Avoid sending full revisions when delta parent is null. Pick prev
981 # in that case. It's tempting to pick p1 in this case, as p1 will
982 # be smaller in the common case. However, computing a delta against
983 # p1 may require resolving the raw text of p1, which could be
984 # expensive. The revlog caches should have prev cached, meaning
985 # less CPU for changegroup generation. There is likely room to add
986 # a flag and/or config option to control this behavior.
987 base = prev
988 elif dp == nullrev:
989 # revlog is configured to use full snapshot for a reason,
990 # stick to full snapshot.
991 base = nullrev
992 elif dp not in (p1, p2, prev):
993 # Pick prev when we can't be sure remote has the base revision.
994 return prev
995 else:
996 base = dp
997
998 if base != nullrev and not store.candelta(base, rev):
999 base = nullrev
1000
1001 return base
956 1002
957 def revchunk(self, store, rev, prev, linknode): 1003 def revchunk(self, store, rev, prev, linknode):
958 if util.safehasattr(self, 'full_nodes'): 1004 if util.safehasattr(self, 'full_nodes'):
959 fn = self._revisiondeltanarrow 1005 fn = self._revisiondeltanarrow
960 else: 1006 else:
1123 linknode=linknode, 1169 linknode=linknode,
1124 flags=flags, 1170 flags=flags,
1125 deltachunks=(diffheader, data), 1171 deltachunks=(diffheader, data),
1126 ) 1172 )
1127 1173
1128 class cg2packer(cg1packer):
1129 def deltaparent(self, store, rev, p1, p2, prev):
1130 # Narrow ellipses mode.
1131 if util.safehasattr(self, 'full_nodes'):
1132 # TODO: send better deltas when in narrow mode.
1133 #
1134 # changegroup.group() loops over revisions to send,
1135 # including revisions we'll skip. What this means is that
1136 # `prev` will be a potentially useless delta base for all
1137 # ellipsis nodes, as the client likely won't have it. In
1138 # the future we should do bookkeeping about which nodes
1139 # have been sent to the client, and try to be
1140 # significantly smarter about delta bases. This is
1141 # slightly tricky because this same code has to work for
1142 # all revlogs, and we don't have the linkrev/linknode here.
1143 return p1
1144
1145 dp = store.deltaparent(rev)
1146 if dp == nullrev and store.storedeltachains:
1147 # Avoid sending full revisions when delta parent is null. Pick prev
1148 # in that case. It's tempting to pick p1 in this case, as p1 will
1149 # be smaller in the common case. However, computing a delta against
1150 # p1 may require resolving the raw text of p1, which could be
1151 # expensive. The revlog caches should have prev cached, meaning
1152 # less CPU for changegroup generation. There is likely room to add
1153 # a flag and/or config option to control this behavior.
1154 base = prev
1155 elif dp == nullrev:
1156 # revlog is configured to use full snapshot for a reason,
1157 # stick to full snapshot.
1158 base = nullrev
1159 elif dp not in (p1, p2, prev):
1160 # Pick prev when we can't be sure remote has the base revision.
1161 return prev
1162 else:
1163 base = dp
1164 if base != nullrev and not store.candelta(base, rev):
1165 base = nullrev
1166 return base
1167
1168 def _makecg1packer(repo, filematcher, bundlecaps): 1174 def _makecg1packer(repo, filematcher, bundlecaps):
1169 builddeltaheader = lambda d: _CHANGEGROUPV1_DELTA_HEADER.pack( 1175 builddeltaheader = lambda d: _CHANGEGROUPV1_DELTA_HEADER.pack(
1170 d.node, d.p1node, d.p2node, d.linknode) 1176 d.node, d.p1node, d.p2node, d.linknode)
1171 1177
1172 return cg1packer(repo, filematcher, b'01', allowreorder=None, 1178 return cg1packer(repo, filematcher, b'01',
1179 useprevdelta=True,
1180 allowreorder=None,
1173 builddeltaheader=builddeltaheader, 1181 builddeltaheader=builddeltaheader,
1174 manifestsend=b'', sendtreemanifests=False, 1182 manifestsend=b'', sendtreemanifests=False,
1175 bundlecaps=bundlecaps) 1183 bundlecaps=bundlecaps)
1176 1184
1177 def _makecg2packer(repo, filematcher, bundlecaps): 1185 def _makecg2packer(repo, filematcher, bundlecaps):
1179 d.node, d.p1node, d.p2node, d.basenode, d.linknode) 1187 d.node, d.p1node, d.p2node, d.basenode, d.linknode)
1180 1188
1181 # Since generaldelta is directly supported by cg2, reordering 1189 # Since generaldelta is directly supported by cg2, reordering
1182 # generally doesn't help, so we disable it by default (treating 1190 # generally doesn't help, so we disable it by default (treating
1183 # bundle.reorder=auto just like bundle.reorder=False). 1191 # bundle.reorder=auto just like bundle.reorder=False).
1184 return cg2packer(repo, filematcher, b'02', allowreorder=False, 1192 return cg1packer(repo, filematcher, b'02',
1193 useprevdelta=False,
1194 allowreorder=False,
1185 builddeltaheader=builddeltaheader, 1195 builddeltaheader=builddeltaheader,
1186 manifestsend=b'', sendtreemanifests=False, 1196 manifestsend=b'', sendtreemanifests=False,
1187 bundlecaps=bundlecaps) 1197 bundlecaps=bundlecaps)
1188 1198
1189 def _makecg3packer(repo, filematcher, bundlecaps): 1199 def _makecg3packer(repo, filematcher, bundlecaps):
1190 builddeltaheader = lambda d: _CHANGEGROUPV3_DELTA_HEADER.pack( 1200 builddeltaheader = lambda d: _CHANGEGROUPV3_DELTA_HEADER.pack(
1191 d.node, d.p1node, d.p2node, d.basenode, d.linknode, d.flags) 1201 d.node, d.p1node, d.p2node, d.basenode, d.linknode, d.flags)
1192 1202
1193 return cg2packer(repo, filematcher, b'03', allowreorder=False, 1203 return cg1packer(repo, filematcher, b'03',
1204 useprevdelta=False,
1205 allowreorder=False,
1194 builddeltaheader=builddeltaheader, 1206 builddeltaheader=builddeltaheader,
1195 manifestsend=closechunk(), sendtreemanifests=True, 1207 manifestsend=closechunk(), sendtreemanifests=True,
1196 bundlecaps=bundlecaps) 1208 bundlecaps=bundlecaps)
1197 1209
1198 _packermap = {'01': (_makecg1packer, cg1unpacker), 1210 _packermap = {'01': (_makecg1packer, cg1unpacker),