comparison mercurial/changegroup.py @ 38975:87b737b78bd0

changegroup: pass function to resolve delta parents into constructor Previously, _deltaparent() encapsulated the logic for all 3 delta parent modes of operation. The choice of delta parent is static for the lifetime of the packer and can be passed into the packer as a callable. So do that. Differential Revision: https://phab.mercurial-scm.org/D4132
author Gregory Szorc <gregory.szorc@gmail.com>
date Fri, 03 Aug 2018 14:16:14 -0700
parents 4f06e0360bad
children f7228c907ef4
comparison
equal deleted inserted replaced
38974:4f06e0360bad 38975:87b737b78bd0
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 cgpacker(object): 522 class cgpacker(object):
523 def __init__(self, repo, filematcher, version, allowreorder, 523 def __init__(self, repo, filematcher, version, allowreorder,
524 useprevdelta, builddeltaheader, manifestsend, 524 deltaparentfn, builddeltaheader, manifestsend,
525 bundlecaps=None, ellipses=False, 525 bundlecaps=None, ellipses=False,
526 shallow=False, ellipsisroots=None, fullnodes=None): 526 shallow=False, ellipsisroots=None, fullnodes=None):
527 """Given a source repo, construct a bundler. 527 """Given a source repo, construct a bundler.
528 528
529 filematcher is a matcher that matches on files to include in the 529 filematcher is a matcher that matches on files to include in the
531 531
532 allowreorder controls whether reordering of revisions is allowed. 532 allowreorder controls whether reordering of revisions is allowed.
533 This value is used when ``bundle.reorder`` is ``auto`` or isn't 533 This value is used when ``bundle.reorder`` is ``auto`` or isn't
534 set. 534 set.
535 535
536 useprevdelta controls whether revisions should always delta against 536 deltaparentfn is a callable that resolves the delta parent for
537 the previous revision in the changegroup. 537 a specific revision.
538 538
539 builddeltaheader is a callable that constructs the header for a group 539 builddeltaheader is a callable that constructs the header for a group
540 delta. 540 delta.
541 541
542 manifestsend is a chunk to send after manifests have been fully emitted. 542 manifestsend is a chunk to send after manifests have been fully emitted.
557 """ 557 """
558 assert filematcher 558 assert filematcher
559 self._filematcher = filematcher 559 self._filematcher = filematcher
560 560
561 self.version = version 561 self.version = version
562 self._useprevdelta = useprevdelta 562 self._deltaparentfn = deltaparentfn
563 self._builddeltaheader = builddeltaheader 563 self._builddeltaheader = builddeltaheader
564 self._manifestsend = manifestsend 564 self._manifestsend = manifestsend
565 self._ellipses = ellipses 565 self._ellipses = ellipses
566 566
567 # Set of capabilities we can use to build the bundle. 567 # Set of capabilities we can use to build the bundle.
967 size += len(chunk) 967 size += len(chunk)
968 yield chunk 968 yield chunk
969 self._verbosenote(_('%8.i %s\n') % (size, fname)) 969 self._verbosenote(_('%8.i %s\n') % (size, fname))
970 progress.complete() 970 progress.complete()
971 971
972 def _deltaparent(self, store, rev, p1, p2, prev):
973 if self._useprevdelta:
974 if not store.candelta(prev, rev):
975 raise error.ProgrammingError(
976 'cg1 should not be used in this case')
977 return prev
978
979 # Narrow ellipses mode.
980 if self._ellipses:
981 # TODO: send better deltas when in narrow mode.
982 #
983 # changegroup.group() loops over revisions to send,
984 # including revisions we'll skip. What this means is that
985 # `prev` will be a potentially useless delta base for all
986 # ellipsis nodes, as the client likely won't have it. In
987 # the future we should do bookkeeping about which nodes
988 # have been sent to the client, and try to be
989 # significantly smarter about delta bases. This is
990 # slightly tricky because this same code has to work for
991 # all revlogs, and we don't have the linkrev/linknode here.
992 return p1
993
994 dp = store.deltaparent(rev)
995 if dp == nullrev and store.storedeltachains:
996 # Avoid sending full revisions when delta parent is null. Pick prev
997 # in that case. It's tempting to pick p1 in this case, as p1 will
998 # be smaller in the common case. However, computing a delta against
999 # p1 may require resolving the raw text of p1, which could be
1000 # expensive. The revlog caches should have prev cached, meaning
1001 # less CPU for changegroup generation. There is likely room to add
1002 # a flag and/or config option to control this behavior.
1003 base = prev
1004 elif dp == nullrev:
1005 # revlog is configured to use full snapshot for a reason,
1006 # stick to full snapshot.
1007 base = nullrev
1008 elif dp not in (p1, p2, prev):
1009 # Pick prev when we can't be sure remote has the base revision.
1010 return prev
1011 else:
1012 base = dp
1013
1014 if base != nullrev and not store.candelta(base, rev):
1015 base = nullrev
1016
1017 return base
1018
1019 def _revchunk(self, store, rev, prev, linknode): 972 def _revchunk(self, store, rev, prev, linknode):
1020 if self._ellipses: 973 if self._ellipses:
1021 fn = self._revisiondeltanarrow 974 fn = self._revisiondeltanarrow
1022 else: 975 else:
1023 fn = self._revisiondeltanormal 976 fn = self._revisiondeltanormal
1035 yield x 988 yield x
1036 989
1037 def _revisiondeltanormal(self, store, rev, prev, linknode): 990 def _revisiondeltanormal(self, store, rev, prev, linknode):
1038 node = store.node(rev) 991 node = store.node(rev)
1039 p1, p2 = store.parentrevs(rev) 992 p1, p2 = store.parentrevs(rev)
1040 base = self._deltaparent(store, rev, p1, p2, prev) 993 base = self._deltaparentfn(store, rev, p1, p2, prev)
1041 994
1042 prefix = '' 995 prefix = ''
1043 if store.iscensored(base) or store.iscensored(rev): 996 if store.iscensored(base) or store.iscensored(rev):
1044 try: 997 try:
1045 delta = store.revision(node, raw=True) 998 delta = store.revision(node, raw=True)
1185 linknode=linknode, 1138 linknode=linknode,
1186 flags=flags, 1139 flags=flags,
1187 deltachunks=(diffheader, data), 1140 deltachunks=(diffheader, data),
1188 ) 1141 )
1189 1142
1143 def _deltaparentprev(store, rev, p1, p2, prev):
1144 """Resolve a delta parent to the previous revision.
1145
1146 Used for version 1 changegroups, which don't support generaldelta.
1147 """
1148 return prev
1149
1150 def _deltaparentgeneraldelta(store, rev, p1, p2, prev):
1151 """Resolve a delta parent when general deltas are supported."""
1152 dp = store.deltaparent(rev)
1153 if dp == nullrev and store.storedeltachains:
1154 # Avoid sending full revisions when delta parent is null. Pick prev
1155 # in that case. It's tempting to pick p1 in this case, as p1 will
1156 # be smaller in the common case. However, computing a delta against
1157 # p1 may require resolving the raw text of p1, which could be
1158 # expensive. The revlog caches should have prev cached, meaning
1159 # less CPU for changegroup generation. There is likely room to add
1160 # a flag and/or config option to control this behavior.
1161 base = prev
1162 elif dp == nullrev:
1163 # revlog is configured to use full snapshot for a reason,
1164 # stick to full snapshot.
1165 base = nullrev
1166 elif dp not in (p1, p2, prev):
1167 # Pick prev when we can't be sure remote has the base revision.
1168 return prev
1169 else:
1170 base = dp
1171
1172 if base != nullrev and not store.candelta(base, rev):
1173 base = nullrev
1174
1175 return base
1176
1177 def _deltaparentellipses(store, rev, p1, p2, prev):
1178 """Resolve a delta parent when in ellipses mode."""
1179 # TODO: send better deltas when in narrow mode.
1180 #
1181 # changegroup.group() loops over revisions to send,
1182 # including revisions we'll skip. What this means is that
1183 # `prev` will be a potentially useless delta base for all
1184 # ellipsis nodes, as the client likely won't have it. In
1185 # the future we should do bookkeeping about which nodes
1186 # have been sent to the client, and try to be
1187 # significantly smarter about delta bases. This is
1188 # slightly tricky because this same code has to work for
1189 # all revlogs, and we don't have the linkrev/linknode here.
1190 return p1
1191
1190 def _makecg1packer(repo, filematcher, bundlecaps, ellipses=False, 1192 def _makecg1packer(repo, filematcher, bundlecaps, ellipses=False,
1191 shallow=False, ellipsisroots=None, fullnodes=None): 1193 shallow=False, ellipsisroots=None, fullnodes=None):
1192 builddeltaheader = lambda d: _CHANGEGROUPV1_DELTA_HEADER.pack( 1194 builddeltaheader = lambda d: _CHANGEGROUPV1_DELTA_HEADER.pack(
1193 d.node, d.p1node, d.p2node, d.linknode) 1195 d.node, d.p1node, d.p2node, d.linknode)
1194 1196
1195 return cgpacker(repo, filematcher, b'01', 1197 return cgpacker(repo, filematcher, b'01',
1196 useprevdelta=True, 1198 deltaparentfn=_deltaparentprev,
1197 allowreorder=None, 1199 allowreorder=None,
1198 builddeltaheader=builddeltaheader, 1200 builddeltaheader=builddeltaheader,
1199 manifestsend=b'', 1201 manifestsend=b'',
1200 bundlecaps=bundlecaps, 1202 bundlecaps=bundlecaps,
1201 ellipses=ellipses, 1203 ellipses=ellipses,
1210 1212
1211 # Since generaldelta is directly supported by cg2, reordering 1213 # Since generaldelta is directly supported by cg2, reordering
1212 # generally doesn't help, so we disable it by default (treating 1214 # generally doesn't help, so we disable it by default (treating
1213 # bundle.reorder=auto just like bundle.reorder=False). 1215 # bundle.reorder=auto just like bundle.reorder=False).
1214 return cgpacker(repo, filematcher, b'02', 1216 return cgpacker(repo, filematcher, b'02',
1215 useprevdelta=False, 1217 deltaparentfn=_deltaparentgeneraldelta,
1216 allowreorder=False, 1218 allowreorder=False,
1217 builddeltaheader=builddeltaheader, 1219 builddeltaheader=builddeltaheader,
1218 manifestsend=b'', 1220 manifestsend=b'',
1219 bundlecaps=bundlecaps, 1221 bundlecaps=bundlecaps,
1220 ellipses=ellipses, 1222 ellipses=ellipses,
1225 def _makecg3packer(repo, filematcher, bundlecaps, ellipses=False, 1227 def _makecg3packer(repo, filematcher, bundlecaps, ellipses=False,
1226 shallow=False, ellipsisroots=None, fullnodes=None): 1228 shallow=False, ellipsisroots=None, fullnodes=None):
1227 builddeltaheader = lambda d: _CHANGEGROUPV3_DELTA_HEADER.pack( 1229 builddeltaheader = lambda d: _CHANGEGROUPV3_DELTA_HEADER.pack(
1228 d.node, d.p1node, d.p2node, d.basenode, d.linknode, d.flags) 1230 d.node, d.p1node, d.p2node, d.basenode, d.linknode, d.flags)
1229 1231
1232 deltaparentfn = (_deltaparentellipses if ellipses
1233 else _deltaparentgeneraldelta)
1234
1230 return cgpacker(repo, filematcher, b'03', 1235 return cgpacker(repo, filematcher, b'03',
1231 useprevdelta=False, 1236 deltaparentfn=deltaparentfn,
1232 allowreorder=False, 1237 allowreorder=False,
1233 builddeltaheader=builddeltaheader, 1238 builddeltaheader=builddeltaheader,
1234 manifestsend=closechunk(), 1239 manifestsend=closechunk(),
1235 bundlecaps=bundlecaps, 1240 bundlecaps=bundlecaps,
1236 ellipses=ellipses, 1241 ellipses=ellipses,