Show More
@@ -73,6 +73,7 b' from . import (' | |||||
73 | repoview, |
|
73 | repoview, | |
74 | requirements, |
|
74 | requirements, | |
75 | revlog, |
|
75 | revlog, | |
|
76 | revlogutils, | |||
76 | revset, |
|
77 | revset, | |
77 | revsetlang, |
|
78 | revsetlang, | |
78 | scmutil, |
|
79 | scmutil, | |
@@ -989,6 +990,65 b' def debugdeltachain(ui, repo, file_=None' | |||||
989 |
|
990 | |||
990 |
|
991 | |||
991 | @command( |
|
992 | @command( | |
|
993 | b'debug-delta-find', | |||
|
994 | cmdutil.debugrevlogopts + cmdutil.formatteropts, | |||
|
995 | _(b'-c|-m|FILE REV'), | |||
|
996 | optionalrepo=True, | |||
|
997 | ) | |||
|
998 | def debugdeltafind(ui, repo, arg_1, arg_2=None, **opts): | |||
|
999 | """display the computation to get to a valid delta for storing REV | |||
|
1000 | ||||
|
1001 | This command will replay the process used to find the "best" delta to store | |||
|
1002 | a revision and display information about all the steps used to get to that | |||
|
1003 | result. | |||
|
1004 | ||||
|
1005 | The revision use the revision number of the target storage (not changelog | |||
|
1006 | revision number). | |||
|
1007 | ||||
|
1008 | note: the process is initiated from a full text of the revision to store. | |||
|
1009 | """ | |||
|
1010 | opts = pycompat.byteskwargs(opts) | |||
|
1011 | if arg_2 is None: | |||
|
1012 | file_ = None | |||
|
1013 | rev = arg_1 | |||
|
1014 | else: | |||
|
1015 | file_ = arg_1 | |||
|
1016 | rev = arg_2 | |||
|
1017 | ||||
|
1018 | rev = int(rev) | |||
|
1019 | ||||
|
1020 | revlog = cmdutil.openrevlog(repo, b'debugdeltachain', file_, opts) | |||
|
1021 | ||||
|
1022 | deltacomputer = deltautil.deltacomputer( | |||
|
1023 | revlog, | |||
|
1024 | write_debug=ui.write, | |||
|
1025 | debug_search=True, | |||
|
1026 | ) | |||
|
1027 | ||||
|
1028 | node = revlog.node(rev) | |||
|
1029 | p1r, p2r = revlog.parentrevs(rev) | |||
|
1030 | p1 = revlog.node(p1r) | |||
|
1031 | p2 = revlog.node(p2r) | |||
|
1032 | btext = [revlog.revision(rev)] | |||
|
1033 | textlen = len(btext[0]) | |||
|
1034 | cachedelta = None | |||
|
1035 | flags = revlog.flags(rev) | |||
|
1036 | ||||
|
1037 | revinfo = revlogutils.revisioninfo( | |||
|
1038 | node, | |||
|
1039 | p1, | |||
|
1040 | p2, | |||
|
1041 | btext, | |||
|
1042 | textlen, | |||
|
1043 | cachedelta, | |||
|
1044 | flags, | |||
|
1045 | ) | |||
|
1046 | ||||
|
1047 | fh = revlog._datafp() | |||
|
1048 | deltacomputer.finddeltainfo(revinfo, fh, target_rev=rev) | |||
|
1049 | ||||
|
1050 | ||||
|
1051 | @command( | |||
992 | b'debugdirstate|debugstate', |
|
1052 | b'debugdirstate|debugstate', | |
993 | [ |
|
1053 | [ | |
994 | ( |
|
1054 | ( |
@@ -931,9 +931,10 b' def _rawgroups(revlog, p1, p2, cachedelt' | |||||
931 |
|
931 | |||
932 |
|
932 | |||
933 | class deltacomputer: |
|
933 | class deltacomputer: | |
934 | def __init__(self, revlog, write_debug=None): |
|
934 | def __init__(self, revlog, write_debug=None, debug_search=False): | |
935 | self.revlog = revlog |
|
935 | self.revlog = revlog | |
936 | self._write_debug = write_debug |
|
936 | self._write_debug = write_debug | |
|
937 | self._debug_search = debug_search | |||
937 |
|
938 | |||
938 | def buildtext(self, revinfo, fh): |
|
939 | def buildtext(self, revinfo, fh): | |
939 | """Builds a fulltext version of a revision |
|
940 | """Builds a fulltext version of a revision | |
@@ -980,6 +981,7 b' class deltacomputer:' | |||||
980 | def _builddeltainfo(self, revinfo, base, fh): |
|
981 | def _builddeltainfo(self, revinfo, base, fh): | |
981 | # can we use the cached delta? |
|
982 | # can we use the cached delta? | |
982 | revlog = self.revlog |
|
983 | revlog = self.revlog | |
|
984 | debug_search = self._write_debug is not None and self._debug_search | |||
983 | chainbase = revlog.chainbase(base) |
|
985 | chainbase = revlog.chainbase(base) | |
984 | if revlog._generaldelta: |
|
986 | if revlog._generaldelta: | |
985 | deltabase = base |
|
987 | deltabase = base | |
@@ -1009,13 +1011,27 b' class deltacomputer:' | |||||
1009 | delta = revinfo.cachedelta[1] |
|
1011 | delta = revinfo.cachedelta[1] | |
1010 | if delta is None: |
|
1012 | if delta is None: | |
1011 | delta = self._builddeltadiff(base, revinfo, fh) |
|
1013 | delta = self._builddeltadiff(base, revinfo, fh) | |
|
1014 | if debug_search: | |||
|
1015 | msg = b"DBG-DELTAS-SEARCH: uncompressed-delta-size=%d\n" | |||
|
1016 | msg %= len(delta) | |||
|
1017 | self._write_debug(msg) | |||
1012 | # snapshotdept need to be neither None nor 0 level snapshot |
|
1018 | # snapshotdept need to be neither None nor 0 level snapshot | |
1013 | if revlog.upperboundcomp is not None and snapshotdepth: |
|
1019 | if revlog.upperboundcomp is not None and snapshotdepth: | |
1014 | lowestrealisticdeltalen = len(delta) // revlog.upperboundcomp |
|
1020 | lowestrealisticdeltalen = len(delta) // revlog.upperboundcomp | |
1015 | snapshotlimit = revinfo.textlen >> snapshotdepth |
|
1021 | snapshotlimit = revinfo.textlen >> snapshotdepth | |
|
1022 | if debug_search: | |||
|
1023 | msg = b"DBG-DELTAS-SEARCH: projected-lower-size=%d\n" | |||
|
1024 | msg %= lowestrealisticdeltalen | |||
|
1025 | self._write_debug(msg) | |||
1016 | if snapshotlimit < lowestrealisticdeltalen: |
|
1026 | if snapshotlimit < lowestrealisticdeltalen: | |
|
1027 | if debug_search: | |||
|
1028 | msg = b"DBG-DELTAS-SEARCH: DISCARDED (snapshot limit)\n" | |||
|
1029 | self._write_debug(msg) | |||
1017 | return None |
|
1030 | return None | |
1018 | if revlog.length(base) < lowestrealisticdeltalen: |
|
1031 | if revlog.length(base) < lowestrealisticdeltalen: | |
|
1032 | if debug_search: | |||
|
1033 | msg = b"DBG-DELTAS-SEARCH: DISCARDED (prev size)\n" | |||
|
1034 | self._write_debug(msg) | |||
1019 | return None |
|
1035 | return None | |
1020 | header, data = revlog.compress(delta) |
|
1036 | header, data = revlog.compress(delta) | |
1021 | deltalen = len(header) + len(data) |
|
1037 | deltalen = len(header) + len(data) | |
@@ -1090,6 +1106,8 b' class deltacomputer:' | |||||
1090 | if self._write_debug is not None: |
|
1106 | if self._write_debug is not None: | |
1091 | start = util.timer() |
|
1107 | start = util.timer() | |
1092 |
|
1108 | |||
|
1109 | debug_search = self._write_debug is not None and self._debug_search | |||
|
1110 | ||||
1093 | # count the number of different delta we tried (for debug purpose) |
|
1111 | # count the number of different delta we tried (for debug purpose) | |
1094 | dbg_try_count = 0 |
|
1112 | dbg_try_count = 0 | |
1095 | # count the number of "search round" we did. (for debug purpose) |
|
1113 | # count the number of "search round" we did. (for debug purpose) | |
@@ -1113,6 +1131,10 b' class deltacomputer:' | |||||
1113 | p2_chain_len = revlog._chaininfo(p2r)[0] |
|
1131 | p2_chain_len = revlog._chaininfo(p2r)[0] | |
1114 | else: |
|
1132 | else: | |
1115 | p2_chain_len = -1 |
|
1133 | p2_chain_len = -1 | |
|
1134 | if debug_search: | |||
|
1135 | msg = b"DBG-DELTAS-SEARCH: SEARCH rev=%d\n" | |||
|
1136 | msg %= target_rev | |||
|
1137 | self._write_debug(msg) | |||
1116 |
|
1138 | |||
1117 | groups = _candidategroups( |
|
1139 | groups = _candidategroups( | |
1118 | self.revlog, revinfo.textlen, p1r, p2r, cachedelta |
|
1140 | self.revlog, revinfo.textlen, p1r, p2r, cachedelta | |
@@ -1120,21 +1142,93 b' class deltacomputer:' | |||||
1120 | candidaterevs = next(groups) |
|
1142 | candidaterevs = next(groups) | |
1121 | while candidaterevs is not None: |
|
1143 | while candidaterevs is not None: | |
1122 | dbg_try_rounds += 1 |
|
1144 | dbg_try_rounds += 1 | |
|
1145 | if debug_search: | |||
|
1146 | prev = None | |||
|
1147 | if deltainfo is not None: | |||
|
1148 | prev = deltainfo.base | |||
|
1149 | ||||
|
1150 | if p1 in candidaterevs or p2 in candidaterevs: | |||
|
1151 | round_type = b"parents" | |||
|
1152 | elif prev is not None and all(c < prev for c in candidaterevs): | |||
|
1153 | round_type = b"refine-down" | |||
|
1154 | elif prev is not None and all(c > prev for c in candidaterevs): | |||
|
1155 | round_type = b"refine-up" | |||
|
1156 | else: | |||
|
1157 | round_type = b"search-down" | |||
|
1158 | msg = b"DBG-DELTAS-SEARCH: ROUND #%d - %d candidates - %s\n" | |||
|
1159 | msg %= (dbg_try_rounds, len(candidaterevs), round_type) | |||
|
1160 | self._write_debug(msg) | |||
1123 | nominateddeltas = [] |
|
1161 | nominateddeltas = [] | |
1124 | if deltainfo is not None: |
|
1162 | if deltainfo is not None: | |
|
1163 | if debug_search: | |||
|
1164 | msg = ( | |||
|
1165 | b"DBG-DELTAS-SEARCH: CONTENDER: rev=%d - length=%d\n" | |||
|
1166 | ) | |||
|
1167 | msg %= (deltainfo.base, deltainfo.deltalen) | |||
|
1168 | self._write_debug(msg) | |||
1125 | # if we already found a good delta, |
|
1169 | # if we already found a good delta, | |
1126 | # challenge it against refined candidates |
|
1170 | # challenge it against refined candidates | |
1127 | nominateddeltas.append(deltainfo) |
|
1171 | nominateddeltas.append(deltainfo) | |
1128 | for candidaterev in candidaterevs: |
|
1172 | for candidaterev in candidaterevs: | |
|
1173 | if debug_search: | |||
|
1174 | msg = b"DBG-DELTAS-SEARCH: CANDIDATE: rev=%d\n" | |||
|
1175 | msg %= candidaterev | |||
|
1176 | self._write_debug(msg) | |||
|
1177 | candidate_type = None | |||
|
1178 | if candidaterev == p1: | |||
|
1179 | candidate_type = b"p1" | |||
|
1180 | elif candidaterev == p2: | |||
|
1181 | candidate_type = b"p2" | |||
|
1182 | elif self.revlog.issnapshot(candidaterev): | |||
|
1183 | candidate_type = b"snapshot-%d" | |||
|
1184 | candidate_type %= self.revlog.snapshotdepth( | |||
|
1185 | candidaterev | |||
|
1186 | ) | |||
|
1187 | ||||
|
1188 | if candidate_type is not None: | |||
|
1189 | msg = b"DBG-DELTAS-SEARCH: type=%s\n" | |||
|
1190 | msg %= candidate_type | |||
|
1191 | self._write_debug(msg) | |||
|
1192 | msg = b"DBG-DELTAS-SEARCH: size=%d\n" | |||
|
1193 | msg %= self.revlog.length(candidaterev) | |||
|
1194 | self._write_debug(msg) | |||
|
1195 | msg = b"DBG-DELTAS-SEARCH: base=%d\n" | |||
|
1196 | msg %= self.revlog.deltaparent(candidaterev) | |||
|
1197 | self._write_debug(msg) | |||
1129 | if candidaterev in excluded_bases: |
|
1198 | if candidaterev in excluded_bases: | |
|
1199 | if debug_search: | |||
|
1200 | msg = b"DBG-DELTAS-SEARCH: EXCLUDED\n" | |||
|
1201 | self._write_debug(msg) | |||
1130 | continue |
|
1202 | continue | |
1131 | if candidaterev >= target_rev: |
|
1203 | if candidaterev >= target_rev: | |
|
1204 | if debug_search: | |||
|
1205 | msg = b"DBG-DELTAS-SEARCH: TOO-HIGH\n" | |||
|
1206 | self._write_debug(msg) | |||
1132 | continue |
|
1207 | continue | |
1133 | dbg_try_count += 1 |
|
1208 | dbg_try_count += 1 | |
|
1209 | ||||
|
1210 | if debug_search: | |||
|
1211 | delta_start = util.timer() | |||
1134 | candidatedelta = self._builddeltainfo(revinfo, candidaterev, fh) |
|
1212 | candidatedelta = self._builddeltainfo(revinfo, candidaterev, fh) | |
|
1213 | if debug_search: | |||
|
1214 | delta_end = util.timer() | |||
|
1215 | msg = b"DBG-DELTAS-SEARCH: delta-search-time=%f\n" | |||
|
1216 | msg %= delta_end - delta_start | |||
|
1217 | self._write_debug(msg) | |||
1135 | if candidatedelta is not None: |
|
1218 | if candidatedelta is not None: | |
1136 | if isgooddeltainfo(self.revlog, candidatedelta, revinfo): |
|
1219 | if isgooddeltainfo(self.revlog, candidatedelta, revinfo): | |
|
1220 | if debug_search: | |||
|
1221 | msg = b"DBG-DELTAS-SEARCH: DELTA: length=%d (GOOD)\n" | |||
|
1222 | msg %= candidatedelta.deltalen | |||
|
1223 | self._write_debug(msg) | |||
1137 | nominateddeltas.append(candidatedelta) |
|
1224 | nominateddeltas.append(candidatedelta) | |
|
1225 | elif debug_search: | |||
|
1226 | msg = b"DBG-DELTAS-SEARCH: DELTA: length=%d (BAD)\n" | |||
|
1227 | msg %= candidatedelta.deltalen | |||
|
1228 | self._write_debug(msg) | |||
|
1229 | elif debug_search: | |||
|
1230 | msg = b"DBG-DELTAS-SEARCH: NO-DELTA\n" | |||
|
1231 | self._write_debug(msg) | |||
1138 | if nominateddeltas: |
|
1232 | if nominateddeltas: | |
1139 | deltainfo = min(nominateddeltas, key=lambda x: x.deltalen) |
|
1233 | deltainfo = min(nominateddeltas, key=lambda x: x.deltalen) | |
1140 | if deltainfo is not None: |
|
1234 | if deltainfo is not None: | |
@@ -1145,7 +1239,7 b' class deltacomputer:' | |||||
1145 | if deltainfo is None: |
|
1239 | if deltainfo is None: | |
1146 | dbg_type = b"full" |
|
1240 | dbg_type = b"full" | |
1147 | deltainfo = self._fullsnapshotinfo(fh, revinfo, target_rev) |
|
1241 | deltainfo = self._fullsnapshotinfo(fh, revinfo, target_rev) | |
1148 | elif deltainfo.snapshotdepth: |
|
1242 | elif deltainfo.snapshotdepth: # pytype: disable=attribute-error | |
1149 | dbg_type = b"snapshot" |
|
1243 | dbg_type = b"snapshot" | |
1150 | else: |
|
1244 | else: | |
1151 | dbg_type = b"delta" |
|
1245 | dbg_type = b"delta" | |
@@ -1161,8 +1255,13 b' class deltacomputer:' | |||||
1161 | 'p1-chain-len': p1_chain_len, |
|
1255 | 'p1-chain-len': p1_chain_len, | |
1162 | 'p2-chain-len': p2_chain_len, |
|
1256 | 'p2-chain-len': p2_chain_len, | |
1163 | } |
|
1257 | } | |
1164 | if deltainfo.snapshotdepth is not None: |
|
1258 | if ( | |
1165 | dbg['snapshot-depth'] = deltainfo.snapshotdepth |
|
1259 | deltainfo.snapshotdepth # pytype: disable=attribute-error | |
|
1260 | is not None | |||
|
1261 | ): | |||
|
1262 | dbg[ | |||
|
1263 | 'snapshot-depth' | |||
|
1264 | ] = deltainfo.snapshotdepth # pytype: disable=attribute-error | |||
1166 | else: |
|
1265 | else: | |
1167 | dbg['snapshot-depth'] = 0 |
|
1266 | dbg['snapshot-depth'] = 0 | |
1168 | target_revlog = b"UNKNOWN" |
|
1267 | target_revlog = b"UNKNOWN" |
@@ -74,6 +74,7 b' Do not show debug commands if there are ' | |||||
74 |
|
74 | |||
75 | Show debug commands if there are no other candidates |
|
75 | Show debug commands if there are no other candidates | |
76 | $ hg debugcomplete debug |
|
76 | $ hg debugcomplete debug | |
|
77 | debug-delta-find | |||
77 | debug-repair-issue6528 |
|
78 | debug-repair-issue6528 | |
78 | debugancestor |
|
79 | debugancestor | |
79 | debugantivirusrunning |
|
80 | debugantivirusrunning | |
@@ -267,6 +268,7 b' Show all commands + options' | |||||
267 | config: untrusted, exp-all-known, edit, local, source, shared, non-shared, global, template |
|
268 | config: untrusted, exp-all-known, edit, local, source, shared, non-shared, global, template | |
268 | continue: dry-run |
|
269 | continue: dry-run | |
269 | copy: forget, after, at-rev, force, include, exclude, dry-run |
|
270 | copy: forget, after, at-rev, force, include, exclude, dry-run | |
|
271 | debug-delta-find: changelog, manifest, dir, template | |||
270 | debug-repair-issue6528: to-report, from-report, paranoid, dry-run |
|
272 | debug-repair-issue6528: to-report, from-report, paranoid, dry-run | |
271 | debugancestor: |
|
273 | debugancestor: | |
272 | debugantivirusrunning: |
|
274 | debugantivirusrunning: |
@@ -978,6 +978,8 b' Test list of internal help commands' | |||||
978 | $ hg help debug |
|
978 | $ hg help debug | |
979 | debug commands (internal and unsupported): |
|
979 | debug commands (internal and unsupported): | |
980 |
|
980 | |||
|
981 | debug-delta-find | |||
|
982 | display the computation to get to a valid delta for storing REV | |||
981 | debug-repair-issue6528 |
|
983 | debug-repair-issue6528 | |
982 | find affected revisions and repair them. See issue6528 for more |
|
984 | find affected revisions and repair them. See issue6528 for more | |
983 | details. |
|
985 | details. |
@@ -149,4 +149,46 b' repeatedly while some of it changes rare' | |||||
149 | deltas against p2 : 63 ( 1.36%) |
|
149 | deltas against p2 : 63 ( 1.36%) | |
150 | deltas against other : 0 ( 0.00%) |
|
150 | deltas against other : 0 ( 0.00%) | |
151 |
|
151 | |||
|
152 | ||||
|
153 | Test `debug-delta-find` | |||
|
154 | ----------------------- | |||
|
155 | ||||
|
156 | $ ls -1 | |||
|
157 | SPARSE-REVLOG-TEST-FILE | |||
|
158 | $ hg debugdeltachain SPARSE-REVLOG-TEST-FILE | grep other | tail -1 | |||
|
159 | 4971 3 5 4930 other 19179 346472 427596 1.23414 15994877 15567281 36.40652 427596 179288 1.00000 5 | |||
|
160 | $ hg debug-delta-find SPARSE-REVLOG-TEST-FILE 4971 | |||
|
161 | DBG-DELTAS-SEARCH: SEARCH rev=4971 | |||
|
162 | DBG-DELTAS-SEARCH: ROUND #1 - 2 candidates - search-down | |||
|
163 | DBG-DELTAS-SEARCH: CANDIDATE: rev=4962 | |||
|
164 | DBG-DELTAS-SEARCH: type=snapshot-4 | |||
|
165 | DBG-DELTAS-SEARCH: size=18296 | |||
|
166 | DBG-DELTAS-SEARCH: base=4930 | |||
|
167 | DBG-DELTAS-SEARCH: uncompressed-delta-size=30377 | |||
|
168 | DBG-DELTAS-SEARCH: delta-search-time=* (glob) | |||
|
169 | DBG-DELTAS-SEARCH: DELTA: length=16872 (BAD) | |||
|
170 | DBG-DELTAS-SEARCH: CANDIDATE: rev=4971 | |||
|
171 | DBG-DELTAS-SEARCH: type=snapshot-4 | |||
|
172 | DBG-DELTAS-SEARCH: size=19179 | |||
|
173 | DBG-DELTAS-SEARCH: base=4930 | |||
|
174 | DBG-DELTAS-SEARCH: TOO-HIGH | |||
|
175 | DBG-DELTAS-SEARCH: ROUND #2 - 1 candidates - search-down | |||
|
176 | DBG-DELTAS-SEARCH: CANDIDATE: rev=4930 | |||
|
177 | DBG-DELTAS-SEARCH: type=snapshot-3 | |||
|
178 | DBG-DELTAS-SEARCH: size=39228 | |||
|
179 | DBG-DELTAS-SEARCH: base=4799 | |||
|
180 | DBG-DELTAS-SEARCH: uncompressed-delta-size=33050 | |||
|
181 | DBG-DELTAS-SEARCH: delta-search-time=* (glob) | |||
|
182 | DBG-DELTAS-SEARCH: DELTA: length=19179 (GOOD) | |||
|
183 | DBG-DELTAS-SEARCH: ROUND #3 - 1 candidates - refine-down | |||
|
184 | DBG-DELTAS-SEARCH: CONTENDER: rev=4930 - length=19179 | |||
|
185 | DBG-DELTAS-SEARCH: CANDIDATE: rev=4799 | |||
|
186 | DBG-DELTAS-SEARCH: type=snapshot-2 | |||
|
187 | DBG-DELTAS-SEARCH: size=50213 | |||
|
188 | DBG-DELTAS-SEARCH: base=4623 | |||
|
189 | DBG-DELTAS-SEARCH: uncompressed-delta-size=82661 | |||
|
190 | DBG-DELTAS-SEARCH: delta-search-time=* (glob) | |||
|
191 | DBG-DELTAS-SEARCH: DELTA: length=49132 (BAD) | |||
|
192 | DBG-DELTAS: FILELOG:SPARSE-REVLOG-TEST-FILE: rev=4971: search-rounds=3 try-count=3 - delta-type=snapshot snap-depth=4 - p1-chain-length=15 p2-chain-length=-1 - duration=* (glob) | |||
|
193 | ||||
152 | $ cd .. |
|
194 | $ cd .. |
General Comments 0
You need to be logged in to leave comments.
Login now