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