Mercurial > hg
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, |