Show More
@@ -116,10 +116,10 b' def readfromstore(self, text):' | |||||
116 | if hgmeta or text.startswith(b'\1\n'): |
|
116 | if hgmeta or text.startswith(b'\1\n'): | |
117 | text = storageutil.packmeta(hgmeta, text) |
|
117 | text = storageutil.packmeta(hgmeta, text) | |
118 |
|
118 | |||
119 |
return (text, True |
|
119 | return (text, True) | |
120 |
|
120 | |||
121 |
|
121 | |||
122 |
def writetostore(self, text |
|
122 | def writetostore(self, text): | |
123 | # hg filelog metadata (includes rename, etc) |
|
123 | # hg filelog metadata (includes rename, etc) | |
124 | hgmeta, offset = storageutil.parsemeta(text) |
|
124 | hgmeta, offset = storageutil.parsemeta(text) | |
125 | if offset and offset > 0: |
|
125 | if offset and offset > 0: |
@@ -155,12 +155,12 b' class remotefilelog(object):' | |||||
155 | # text passed to "addrevision" includes hg filelog metadata header |
|
155 | # text passed to "addrevision" includes hg filelog metadata header | |
156 | if node is None: |
|
156 | if node is None: | |
157 | node = storageutil.hashrevisionsha1(text, p1, p2) |
|
157 | node = storageutil.hashrevisionsha1(text, p1, p2) | |
158 | if sidedata is None: |
|
|||
159 | sidedata = {} |
|
|||
160 |
|
158 | |||
161 | meta, metaoffset = storageutil.parsemeta(text) |
|
159 | meta, metaoffset = storageutil.parsemeta(text) | |
162 | rawtext, validatehash = flagutil.processflagswrite( |
|
160 | rawtext, validatehash = flagutil.processflagswrite( | |
163 | self, text, flags, sidedata=sidedata |
|
161 | self, | |
|
162 | text, | |||
|
163 | flags, | |||
164 | ) |
|
164 | ) | |
165 | return self.addrawrevision( |
|
165 | return self.addrawrevision( | |
166 | rawtext, |
|
166 | rawtext, |
@@ -120,10 +120,10 b" rustrevlog = policy.importrust('revlog')" | |||||
120 |
|
120 | |||
121 | # Flag processors for REVIDX_ELLIPSIS. |
|
121 | # Flag processors for REVIDX_ELLIPSIS. | |
122 | def ellipsisreadprocessor(rl, text): |
|
122 | def ellipsisreadprocessor(rl, text): | |
123 |
return text, False |
|
123 | return text, False | |
124 |
|
124 | |||
125 |
|
125 | |||
126 |
def ellipsiswriteprocessor(rl, text |
|
126 | def ellipsiswriteprocessor(rl, text): | |
127 | return text, False |
|
127 | return text, False | |
128 |
|
128 | |||
129 |
|
129 | |||
@@ -554,8 +554,6 b' class revlog(object):' | |||||
554 | if self._mmaplargeindex and b'mmapindexthreshold' in opts: |
|
554 | if self._mmaplargeindex and b'mmapindexthreshold' in opts: | |
555 | mmapindexthreshold = opts[b'mmapindexthreshold'] |
|
555 | mmapindexthreshold = opts[b'mmapindexthreshold'] | |
556 | self.hassidedata = bool(opts.get(b'side-data', False)) |
|
556 | self.hassidedata = bool(opts.get(b'side-data', False)) | |
557 | if self.hassidedata: |
|
|||
558 | self._flagprocessors[REVIDX_SIDEDATA] = sidedatautil.processors |
|
|||
559 | self._sparserevlog = bool(opts.get(b'sparse-revlog', False)) |
|
557 | self._sparserevlog = bool(opts.get(b'sparse-revlog', False)) | |
560 | withsparseread = bool(opts.get(b'with-sparse-read', False)) |
|
558 | withsparseread = bool(opts.get(b'with-sparse-read', False)) | |
561 | # sparse-revlog forces sparse-read |
|
559 | # sparse-revlog forces sparse-read | |
@@ -856,6 +854,11 b' class revlog(object):' | |||||
856 | def length(self, rev): |
|
854 | def length(self, rev): | |
857 | return self.index[rev][1] |
|
855 | return self.index[rev][1] | |
858 |
|
856 | |||
|
857 | def sidedata_length(self, rev): | |||
|
858 | if self.version & 0xFFFF != REVLOGV2: | |||
|
859 | return 0 | |||
|
860 | return self.index[rev][9] | |||
|
861 | ||||
859 | def rawsize(self, rev): |
|
862 | def rawsize(self, rev): | |
860 | """return the length of the uncompressed text for a given revision""" |
|
863 | """return the length of the uncompressed text for a given revision""" | |
861 | l = self.index[rev][2] |
|
864 | l = self.index[rev][2] | |
@@ -917,7 +920,7 b' class revlog(object):' | |||||
917 | # Derived from index values. |
|
920 | # Derived from index values. | |
918 |
|
921 | |||
919 | def end(self, rev): |
|
922 | def end(self, rev): | |
920 | return self.start(rev) + self.length(rev) |
|
923 | return self.start(rev) + self.length(rev) + self.sidedata_length(rev) | |
921 |
|
924 | |||
922 | def parents(self, node): |
|
925 | def parents(self, node): | |
923 | i = self.index |
|
926 | i = self.index | |
@@ -1853,7 +1856,7 b' class revlog(object):' | |||||
1853 | elif operation == b'read': |
|
1856 | elif operation == b'read': | |
1854 | return flagutil.processflagsread(self, text, flags) |
|
1857 | return flagutil.processflagsread(self, text, flags) | |
1855 | else: # write operation |
|
1858 | else: # write operation | |
1856 |
return flagutil.processflagswrite(self, text, flags |
|
1859 | return flagutil.processflagswrite(self, text, flags) | |
1857 |
|
1860 | |||
1858 | def revision(self, nodeorrev, _df=None, raw=False): |
|
1861 | def revision(self, nodeorrev, _df=None, raw=False): | |
1859 | """return an uncompressed revision of a given node or revision |
|
1862 | """return an uncompressed revision of a given node or revision | |
@@ -1898,10 +1901,17 b' class revlog(object):' | |||||
1898 | # revision or might need to be processed to retrieve the revision. |
|
1901 | # revision or might need to be processed to retrieve the revision. | |
1899 | rev, rawtext, validated = self._rawtext(node, rev, _df=_df) |
|
1902 | rev, rawtext, validated = self._rawtext(node, rev, _df=_df) | |
1900 |
|
1903 | |||
|
1904 | if self.version & 0xFFFF == REVLOGV2: | |||
|
1905 | if rev is None: | |||
|
1906 | rev = self.rev(node) | |||
|
1907 | sidedata = self._sidedata(rev) | |||
|
1908 | else: | |||
|
1909 | sidedata = {} | |||
|
1910 | ||||
1901 | if raw and validated: |
|
1911 | if raw and validated: | |
1902 | # if we don't want to process the raw text and that raw |
|
1912 | # if we don't want to process the raw text and that raw | |
1903 | # text is cached, we can exit early. |
|
1913 | # text is cached, we can exit early. | |
1904 |
return rawtext, |
|
1914 | return rawtext, sidedata | |
1905 | if rev is None: |
|
1915 | if rev is None: | |
1906 | rev = self.rev(node) |
|
1916 | rev = self.rev(node) | |
1907 | # the revlog's flag for this revision |
|
1917 | # the revlog's flag for this revision | |
@@ -1910,20 +1920,14 b' class revlog(object):' | |||||
1910 |
|
1920 | |||
1911 | if validated and flags == REVIDX_DEFAULT_FLAGS: |
|
1921 | if validated and flags == REVIDX_DEFAULT_FLAGS: | |
1912 | # no extra flags set, no flag processor runs, text = rawtext |
|
1922 | # no extra flags set, no flag processor runs, text = rawtext | |
1913 |
return rawtext, |
|
1923 | return rawtext, sidedata | |
1914 |
|
1924 | |||
1915 | sidedata = {} |
|
|||
1916 | if raw: |
|
1925 | if raw: | |
1917 | validatehash = flagutil.processflagsraw(self, rawtext, flags) |
|
1926 | validatehash = flagutil.processflagsraw(self, rawtext, flags) | |
1918 | text = rawtext |
|
1927 | text = rawtext | |
1919 | else: |
|
1928 | else: | |
1920 | try: |
|
1929 | r = flagutil.processflagsread(self, rawtext, flags) | |
1921 | r = flagutil.processflagsread(self, rawtext, flags) |
|
1930 | text, validatehash = r | |
1922 | except error.SidedataHashError as exc: |
|
|||
1923 | msg = _(b"integrity check failed on %s:%s sidedata key %d") |
|
|||
1924 | msg %= (self.indexfile, pycompat.bytestr(rev), exc.sidedatakey) |
|
|||
1925 | raise error.RevlogError(msg) |
|
|||
1926 | text, validatehash, sidedata = r |
|
|||
1927 | if validatehash: |
|
1931 | if validatehash: | |
1928 | self.checkhash(text, node, rev=rev) |
|
1932 | self.checkhash(text, node, rev=rev) | |
1929 | if not validated: |
|
1933 | if not validated: | |
@@ -1974,6 +1978,21 b' class revlog(object):' | |||||
1974 | del basetext # let us have a chance to free memory early |
|
1978 | del basetext # let us have a chance to free memory early | |
1975 | return (rev, rawtext, False) |
|
1979 | return (rev, rawtext, False) | |
1976 |
|
1980 | |||
|
1981 | def _sidedata(self, rev): | |||
|
1982 | """Return the sidedata for a given revision number.""" | |||
|
1983 | index_entry = self.index[rev] | |||
|
1984 | sidedata_offset = index_entry[8] | |||
|
1985 | sidedata_size = index_entry[9] | |||
|
1986 | ||||
|
1987 | if self._inline: | |||
|
1988 | sidedata_offset += self._io.size * (1 + rev) | |||
|
1989 | if sidedata_size == 0: | |||
|
1990 | return {} | |||
|
1991 | ||||
|
1992 | segment = self._getsegment(sidedata_offset, sidedata_size) | |||
|
1993 | sidedata = sidedatautil.deserialize_sidedata(segment) | |||
|
1994 | return sidedata | |||
|
1995 | ||||
1977 | def rawdata(self, nodeorrev, _df=None): |
|
1996 | def rawdata(self, nodeorrev, _df=None): | |
1978 | """return an uncompressed raw data of a given node or revision number. |
|
1997 | """return an uncompressed raw data of a given node or revision number. | |
1979 |
|
1998 | |||
@@ -2107,20 +2126,15 b' class revlog(object):' | |||||
2107 |
|
2126 | |||
2108 | if sidedata is None: |
|
2127 | if sidedata is None: | |
2109 | sidedata = {} |
|
2128 | sidedata = {} | |
2110 | flags = flags & ~REVIDX_SIDEDATA |
|
|||
2111 | elif not self.hassidedata: |
|
2129 | elif not self.hassidedata: | |
2112 | raise error.ProgrammingError( |
|
2130 | raise error.ProgrammingError( | |
2113 | _(b"trying to add sidedata to a revlog who don't support them") |
|
2131 | _(b"trying to add sidedata to a revlog who don't support them") | |
2114 | ) |
|
2132 | ) | |
2115 | else: |
|
|||
2116 | flags |= REVIDX_SIDEDATA |
|
|||
2117 |
|
2133 | |||
2118 | if flags: |
|
2134 | if flags: | |
2119 | node = node or self.hash(text, p1, p2) |
|
2135 | node = node or self.hash(text, p1, p2) | |
2120 |
|
2136 | |||
2121 | rawtext, validatehash = flagutil.processflagswrite( |
|
2137 | rawtext, validatehash = flagutil.processflagswrite(self, text, flags) | |
2122 | self, text, flags, sidedata=sidedata |
|
|||
2123 | ) |
|
|||
2124 |
|
2138 | |||
2125 | # If the flag processor modifies the revision data, ignore any provided |
|
2139 | # If the flag processor modifies the revision data, ignore any provided | |
2126 | # cachedelta. |
|
2140 | # cachedelta. | |
@@ -2153,6 +2167,7 b' class revlog(object):' | |||||
2153 | flags, |
|
2167 | flags, | |
2154 | cachedelta=cachedelta, |
|
2168 | cachedelta=cachedelta, | |
2155 | deltacomputer=deltacomputer, |
|
2169 | deltacomputer=deltacomputer, | |
|
2170 | sidedata=sidedata, | |||
2156 | ) |
|
2171 | ) | |
2157 |
|
2172 | |||
2158 | def addrawrevision( |
|
2173 | def addrawrevision( | |
@@ -2166,6 +2181,7 b' class revlog(object):' | |||||
2166 | flags, |
|
2181 | flags, | |
2167 | cachedelta=None, |
|
2182 | cachedelta=None, | |
2168 | deltacomputer=None, |
|
2183 | deltacomputer=None, | |
|
2184 | sidedata=None, | |||
2169 | ): |
|
2185 | ): | |
2170 | """add a raw revision with known flags, node and parents |
|
2186 | """add a raw revision with known flags, node and parents | |
2171 | useful when reusing a revision not stored in this revlog (ex: received |
|
2187 | useful when reusing a revision not stored in this revlog (ex: received | |
@@ -2188,6 +2204,7 b' class revlog(object):' | |||||
2188 | ifh, |
|
2204 | ifh, | |
2189 | dfh, |
|
2205 | dfh, | |
2190 | deltacomputer=deltacomputer, |
|
2206 | deltacomputer=deltacomputer, | |
|
2207 | sidedata=sidedata, | |||
2191 | ) |
|
2208 | ) | |
2192 | finally: |
|
2209 | finally: | |
2193 | if dfh: |
|
2210 | if dfh: | |
@@ -2281,6 +2298,7 b' class revlog(object):' | |||||
2281 | dfh, |
|
2298 | dfh, | |
2282 | alwayscache=False, |
|
2299 | alwayscache=False, | |
2283 | deltacomputer=None, |
|
2300 | deltacomputer=None, | |
|
2301 | sidedata=None, | |||
2284 | ): |
|
2302 | ): | |
2285 | """internal function to add revisions to the log |
|
2303 | """internal function to add revisions to the log | |
2286 |
|
2304 | |||
@@ -2350,6 +2368,16 b' class revlog(object):' | |||||
2350 |
|
2368 | |||
2351 | deltainfo = deltacomputer.finddeltainfo(revinfo, fh) |
|
2369 | deltainfo = deltacomputer.finddeltainfo(revinfo, fh) | |
2352 |
|
2370 | |||
|
2371 | if sidedata: | |||
|
2372 | serialized_sidedata = sidedatautil.serialize_sidedata(sidedata) | |||
|
2373 | sidedata_offset = offset + deltainfo.deltalen | |||
|
2374 | else: | |||
|
2375 | serialized_sidedata = b"" | |||
|
2376 | # Don't store the offset if the sidedata is empty, that way | |||
|
2377 | # we can easily detect empty sidedata and they will be no different | |||
|
2378 | # than ones we manually add. | |||
|
2379 | sidedata_offset = 0 | |||
|
2380 | ||||
2353 | e = ( |
|
2381 | e = ( | |
2354 | offset_type(offset, flags), |
|
2382 | offset_type(offset, flags), | |
2355 | deltainfo.deltalen, |
|
2383 | deltainfo.deltalen, | |
@@ -2359,18 +2387,24 b' class revlog(object):' | |||||
2359 | p1r, |
|
2387 | p1r, | |
2360 | p2r, |
|
2388 | p2r, | |
2361 | node, |
|
2389 | node, | |
2362 |
|
|
2390 | sidedata_offset, | |
2363 | 0, |
|
2391 | len(serialized_sidedata), | |
2364 | ) |
|
2392 | ) | |
2365 |
|
2393 | |||
2366 | if self.version & 0xFFFF != REVLOGV2: |
|
2394 | if self.version & 0xFFFF != REVLOGV2: | |
2367 | e = e[:8] |
|
2395 | e = e[:8] | |
2368 |
|
2396 | |||
2369 | self.index.append(e) |
|
2397 | self.index.append(e) | |
2370 |
|
||||
2371 | entry = self._io.packentry(e, self.node, self.version, curr) |
|
2398 | entry = self._io.packentry(e, self.node, self.version, curr) | |
2372 | self._writeentry( |
|
2399 | self._writeentry( | |
2373 | transaction, ifh, dfh, entry, deltainfo.data, link, offset |
|
2400 | transaction, | |
|
2401 | ifh, | |||
|
2402 | dfh, | |||
|
2403 | entry, | |||
|
2404 | deltainfo.data, | |||
|
2405 | link, | |||
|
2406 | offset, | |||
|
2407 | serialized_sidedata, | |||
2374 | ) |
|
2408 | ) | |
2375 |
|
2409 | |||
2376 | rawtext = btext[0] |
|
2410 | rawtext = btext[0] | |
@@ -2383,7 +2417,9 b' class revlog(object):' | |||||
2383 | self._chainbasecache[curr] = deltainfo.chainbase |
|
2417 | self._chainbasecache[curr] = deltainfo.chainbase | |
2384 | return curr |
|
2418 | return curr | |
2385 |
|
2419 | |||
2386 | def _writeentry(self, transaction, ifh, dfh, entry, data, link, offset): |
|
2420 | def _writeentry( | |
|
2421 | self, transaction, ifh, dfh, entry, data, link, offset, sidedata | |||
|
2422 | ): | |||
2387 | # Files opened in a+ mode have inconsistent behavior on various |
|
2423 | # Files opened in a+ mode have inconsistent behavior on various | |
2388 | # platforms. Windows requires that a file positioning call be made |
|
2424 | # platforms. Windows requires that a file positioning call be made | |
2389 | # when the file handle transitions between reads and writes. See |
|
2425 | # when the file handle transitions between reads and writes. See | |
@@ -2407,6 +2443,8 b' class revlog(object):' | |||||
2407 | if data[0]: |
|
2443 | if data[0]: | |
2408 | dfh.write(data[0]) |
|
2444 | dfh.write(data[0]) | |
2409 | dfh.write(data[1]) |
|
2445 | dfh.write(data[1]) | |
|
2446 | if sidedata: | |||
|
2447 | dfh.write(sidedata) | |||
2410 | ifh.write(entry) |
|
2448 | ifh.write(entry) | |
2411 | else: |
|
2449 | else: | |
2412 | offset += curr * self._io.size |
|
2450 | offset += curr * self._io.size | |
@@ -2414,6 +2452,8 b' class revlog(object):' | |||||
2414 | ifh.write(entry) |
|
2452 | ifh.write(entry) | |
2415 | ifh.write(data[0]) |
|
2453 | ifh.write(data[0]) | |
2416 | ifh.write(data[1]) |
|
2454 | ifh.write(data[1]) | |
|
2455 | if sidedata: | |||
|
2456 | ifh.write(sidedata) | |||
2417 | self._enforceinlinesize(transaction, ifh) |
|
2457 | self._enforceinlinesize(transaction, ifh) | |
2418 | nodemaputil.setup_persistent_nodemap(transaction, self) |
|
2458 | nodemaputil.setup_persistent_nodemap(transaction, self) | |
2419 |
|
2459 |
@@ -84,7 +84,7 b' def insertflagprocessor(flag, processor,' | |||||
84 | flagprocessors[flag] = processor |
|
84 | flagprocessors[flag] = processor | |
85 |
|
85 | |||
86 |
|
86 | |||
87 |
def processflagswrite(revlog, text, flags |
|
87 | def processflagswrite(revlog, text, flags): | |
88 | """Inspect revision data flags and applies write transformations defined |
|
88 | """Inspect revision data flags and applies write transformations defined | |
89 | by registered flag processors. |
|
89 | by registered flag processors. | |
90 |
|
90 | |||
@@ -100,9 +100,12 b' def processflagswrite(revlog, text, flag' | |||||
100 | processed text and ``validatehash`` is a bool indicating whether the |
|
100 | processed text and ``validatehash`` is a bool indicating whether the | |
101 | returned text should be checked for hash integrity. |
|
101 | returned text should be checked for hash integrity. | |
102 | """ |
|
102 | """ | |
103 | return _processflagsfunc(revlog, text, flags, b'write', sidedata=sidedata)[ |
|
103 | return _processflagsfunc( | |
104 |
|
|
104 | revlog, | |
105 | ] |
|
105 | text, | |
|
106 | flags, | |||
|
107 | b'write', | |||
|
108 | )[:2] | |||
106 |
|
109 | |||
107 |
|
110 | |||
108 | def processflagsread(revlog, text, flags): |
|
111 | def processflagsread(revlog, text, flags): | |
@@ -145,14 +148,14 b' def processflagsraw(revlog, text, flags)' | |||||
145 | return _processflagsfunc(revlog, text, flags, b'raw')[1] |
|
148 | return _processflagsfunc(revlog, text, flags, b'raw')[1] | |
146 |
|
149 | |||
147 |
|
150 | |||
148 |
def _processflagsfunc(revlog, text, flags, operation |
|
151 | def _processflagsfunc(revlog, text, flags, operation): | |
149 | """internal function to process flag on a revlog |
|
152 | """internal function to process flag on a revlog | |
150 |
|
153 | |||
151 | This function is private to this module, code should never needs to call it |
|
154 | This function is private to this module, code should never needs to call it | |
152 | directly.""" |
|
155 | directly.""" | |
153 | # fast path: no flag processors will run |
|
156 | # fast path: no flag processors will run | |
154 | if flags == 0: |
|
157 | if flags == 0: | |
155 |
return text, True |
|
158 | return text, True | |
156 | if operation not in (b'read', b'write', b'raw'): |
|
159 | if operation not in (b'read', b'write', b'raw'): | |
157 | raise error.ProgrammingError(_(b"invalid '%s' operation") % operation) |
|
160 | raise error.ProgrammingError(_(b"invalid '%s' operation") % operation) | |
158 | # Check all flags are known. |
|
161 | # Check all flags are known. | |
@@ -168,7 +171,6 b' def _processflagsfunc(revlog, text, flag' | |||||
168 | if operation == b'write': |
|
171 | if operation == b'write': | |
169 | orderedflags = reversed(orderedflags) |
|
172 | orderedflags = reversed(orderedflags) | |
170 |
|
173 | |||
171 | outsidedata = {} |
|
|||
172 | for flag in orderedflags: |
|
174 | for flag in orderedflags: | |
173 | # If a flagprocessor has been registered for a known flag, apply the |
|
175 | # If a flagprocessor has been registered for a known flag, apply the | |
174 | # related operation transform and update result tuple. |
|
176 | # related operation transform and update result tuple. | |
@@ -186,10 +188,9 b' def _processflagsfunc(revlog, text, flag' | |||||
186 | if operation == b'raw': |
|
188 | if operation == b'raw': | |
187 | vhash = rawtransform(revlog, text) |
|
189 | vhash = rawtransform(revlog, text) | |
188 | elif operation == b'read': |
|
190 | elif operation == b'read': | |
189 |
text, vhash |
|
191 | text, vhash = readtransform(revlog, text) | |
190 | outsidedata.update(s) |
|
|||
191 | else: # write operation |
|
192 | else: # write operation | |
192 |
text, vhash = writetransform(revlog, text |
|
193 | text, vhash = writetransform(revlog, text) | |
193 | validatehash = validatehash and vhash |
|
194 | validatehash = validatehash and vhash | |
194 |
|
195 | |||
195 |
return text, validatehash |
|
196 | return text, validatehash |
@@ -13,9 +13,8 b' important information related to a chang' | |||||
13 | The current implementation is experimental and subject to changes. Do not rely |
|
13 | The current implementation is experimental and subject to changes. Do not rely | |
14 | on it in production. |
|
14 | on it in production. | |
15 |
|
15 | |||
16 |
Sidedata are stored in the revlog itself, |
|
16 | Sidedata are stored in the revlog itself, thanks to a new version of the | |
17 | are inserted and removed from it using the flagprocessors mechanism. The following |
|
17 | revlog. The following format is currently used:: | |
18 | format is currently used:: |
|
|||
19 |
|
18 | |||
20 | initial header: |
|
19 | initial header: | |
21 | <number of sidedata; 2 bytes> |
|
20 | <number of sidedata; 2 bytes> | |
@@ -60,48 +59,35 b" SIDEDATA_HEADER = struct.Struct('>H')" | |||||
60 | SIDEDATA_ENTRY = struct.Struct('>HL20s') |
|
59 | SIDEDATA_ENTRY = struct.Struct('>HL20s') | |
61 |
|
60 | |||
62 |
|
61 | |||
63 | def sidedatawriteprocessor(rl, text, sidedata): |
|
62 | def serialize_sidedata(sidedata): | |
64 | sidedata = list(sidedata.items()) |
|
63 | sidedata = list(sidedata.items()) | |
65 | sidedata.sort() |
|
64 | sidedata.sort() | |
66 |
|
|
65 | buf = [SIDEDATA_HEADER.pack(len(sidedata))] | |
67 | for key, value in sidedata: |
|
66 | for key, value in sidedata: | |
68 | digest = hashutil.sha1(value).digest() |
|
67 | digest = hashutil.sha1(value).digest() | |
69 |
|
|
68 | buf.append(SIDEDATA_ENTRY.pack(key, len(value), digest)) | |
70 | for key, value in sidedata: |
|
69 | for key, value in sidedata: | |
71 |
|
|
70 | buf.append(value) | |
72 | rawtext.append(bytes(text)) |
|
71 | buf = b''.join(buf) | |
73 | return b''.join(rawtext), False |
|
72 | return buf | |
74 |
|
73 | |||
75 |
|
74 | |||
76 | def sidedatareadprocessor(rl, text): |
|
75 | def deserialize_sidedata(blob): | |
77 | sidedata = {} |
|
76 | sidedata = {} | |
78 | offset = 0 |
|
77 | offset = 0 | |
79 |
(nbentry,) = SIDEDATA_HEADER.unpack( |
|
78 | (nbentry,) = SIDEDATA_HEADER.unpack(blob[: SIDEDATA_HEADER.size]) | |
80 | offset += SIDEDATA_HEADER.size |
|
79 | offset += SIDEDATA_HEADER.size | |
81 | dataoffset = SIDEDATA_HEADER.size + (SIDEDATA_ENTRY.size * nbentry) |
|
80 | dataoffset = SIDEDATA_HEADER.size + (SIDEDATA_ENTRY.size * nbentry) | |
82 | for i in range(nbentry): |
|
81 | for i in range(nbentry): | |
83 | nextoffset = offset + SIDEDATA_ENTRY.size |
|
82 | nextoffset = offset + SIDEDATA_ENTRY.size | |
84 |
key, size, storeddigest = SIDEDATA_ENTRY.unpack( |
|
83 | key, size, storeddigest = SIDEDATA_ENTRY.unpack(blob[offset:nextoffset]) | |
85 | offset = nextoffset |
|
84 | offset = nextoffset | |
86 | # read the data associated with that entry |
|
85 | # read the data associated with that entry | |
87 | nextdataoffset = dataoffset + size |
|
86 | nextdataoffset = dataoffset + size | |
88 |
entrytext = |
|
87 | entrytext = bytes(blob[dataoffset:nextdataoffset]) | |
89 | readdigest = hashutil.sha1(entrytext).digest() |
|
88 | readdigest = hashutil.sha1(entrytext).digest() | |
90 | if storeddigest != readdigest: |
|
89 | if storeddigest != readdigest: | |
91 | raise error.SidedataHashError(key, storeddigest, readdigest) |
|
90 | raise error.SidedataHashError(key, storeddigest, readdigest) | |
92 | sidedata[key] = entrytext |
|
91 | sidedata[key] = entrytext | |
93 | dataoffset = nextdataoffset |
|
92 | dataoffset = nextdataoffset | |
94 | text = text[dataoffset:] |
|
93 | return sidedata | |
95 | return text, True, sidedata |
|
|||
96 |
|
||||
97 |
|
||||
98 | def sidedatarawprocessor(rl, text): |
|
|||
99 | # side data modifies rawtext and prevent rawtext hash validation |
|
|||
100 | return False |
|
|||
101 |
|
||||
102 |
|
||||
103 | processors = ( |
|
|||
104 | sidedatareadprocessor, |
|
|||
105 | sidedatawriteprocessor, |
|
|||
106 | sidedatarawprocessor, |
|
|||
107 | ) |
|
@@ -31,28 +31,28 b' def bypass(self, text):' | |||||
31 | return False |
|
31 | return False | |
32 |
|
32 | |||
33 |
|
33 | |||
34 |
def noopdonothing(self, text |
|
34 | def noopdonothing(self, text): | |
35 | return (text, True) |
|
35 | return (text, True) | |
36 |
|
36 | |||
37 |
|
37 | |||
38 | def noopdonothingread(self, text): |
|
38 | def noopdonothingread(self, text): | |
39 |
return (text, True |
|
39 | return (text, True) | |
40 |
|
40 | |||
41 |
|
41 | |||
42 |
def b64encode(self, text |
|
42 | def b64encode(self, text): | |
43 | return (base64.b64encode(text), False) |
|
43 | return (base64.b64encode(text), False) | |
44 |
|
44 | |||
45 |
|
45 | |||
46 | def b64decode(self, text): |
|
46 | def b64decode(self, text): | |
47 |
return (base64.b64decode(text), True |
|
47 | return (base64.b64decode(text), True) | |
48 |
|
48 | |||
49 |
|
49 | |||
50 |
def gzipcompress(self, text |
|
50 | def gzipcompress(self, text): | |
51 | return (zlib.compress(text), False) |
|
51 | return (zlib.compress(text), False) | |
52 |
|
52 | |||
53 |
|
53 | |||
54 | def gzipdecompress(self, text): |
|
54 | def gzipdecompress(self, text): | |
55 |
return (zlib.decompress(text), True |
|
55 | return (zlib.decompress(text), True) | |
56 |
|
56 | |||
57 |
|
57 | |||
58 | def supportedoutgoingversions(orig, repo): |
|
58 | def supportedoutgoingversions(orig, repo): |
@@ -300,7 +300,7 b' class filestorage(object):' | |||||
300 | text = rawtext |
|
300 | text = rawtext | |
301 | else: |
|
301 | else: | |
302 | r = flagutil.processflagsread(self, rawtext, flags) |
|
302 | r = flagutil.processflagsread(self, rawtext, flags) | |
303 |
text, validatehash |
|
303 | text, validatehash = r | |
304 | if validatehash: |
|
304 | if validatehash: | |
305 | self.checkhash(text, node, rev=rev) |
|
305 | self.checkhash(text, node, rev=rev) | |
306 |
|
306 |
@@ -271,12 +271,13 b' copy information on to the filelog' | |||||
271 | $ hg ci --amend -m 'copy a to j, v2' |
|
271 | $ hg ci --amend -m 'copy a to j, v2' | |
272 | saved backup bundle to $TESTTMP/repo/.hg/strip-backup/*-*-amend.hg (glob) |
|
272 | saved backup bundle to $TESTTMP/repo/.hg/strip-backup/*-*-amend.hg (glob) | |
273 | $ hg debugsidedata -c -v -- -1 |
|
273 | $ hg debugsidedata -c -v -- -1 | |
274 | 1 sidedata entries |
|
274 | 1 sidedata entries (missing-correct-output !) | |
275 | entry-0014 size 24 |
|
275 | entry-0014 size 24 (missing-correct-output !) | |
276 | '\x00\x00\x00\x02\x00\x00\x00\x00\x01\x00\x00\x00\x00\x06\x00\x00\x00\x02\x00\x00\x00\x00aj' |
|
276 | '\x00\x00\x00\x02\x00\x00\x00\x00\x01\x00\x00\x00\x00\x06\x00\x00\x00\x02\x00\x00\x00\x00aj' (missing-correct-output !) | |
277 | #endif |
|
277 | #endif | |
278 | $ hg showcopies --config experimental.copies.read-from=filelog-only |
|
278 | $ hg showcopies --config experimental.copies.read-from=filelog-only | |
279 | a -> j |
|
279 | a -> j (sidedata missing-correct-output !) | |
|
280 | a -> j (no-sidedata !) | |||
280 | The entries should be written to extras even if they're empty (so the client |
|
281 | The entries should be written to extras even if they're empty (so the client | |
281 | won't have to fall back to reading from filelogs) |
|
282 | won't have to fall back to reading from filelogs) | |
282 | $ echo x >> j |
|
283 | $ echo x >> j | |
@@ -354,7 +355,8 b' with no fallback.' | |||||
354 | saved backup bundle to $TESTTMP/rebase-rename/.hg/strip-backup/*-*-rebase.hg (glob) |
|
355 | saved backup bundle to $TESTTMP/rebase-rename/.hg/strip-backup/*-*-rebase.hg (glob) | |
355 | $ hg st --change . --copies |
|
356 | $ hg st --change . --copies | |
356 | A b |
|
357 | A b | |
357 | a |
|
358 | a (sidedata missing-correct-output !) | |
|
359 | a (no-sidedata !) | |||
358 | R a |
|
360 | R a | |
359 | $ cd .. |
|
361 | $ cd .. | |
360 |
|
362 |
@@ -51,10 +51,10 b' def abort(msg):' | |||||
51 | def readprocessor(self, rawtext): |
|
51 | def readprocessor(self, rawtext): | |
52 | # True: the returned text could be used to verify hash |
|
52 | # True: the returned text could be used to verify hash | |
53 | text = rawtext[len(_extheader) :].replace(b'i', b'1') |
|
53 | text = rawtext[len(_extheader) :].replace(b'i', b'1') | |
54 |
return text, True |
|
54 | return text, True | |
55 |
|
55 | |||
56 |
|
56 | |||
57 |
def writeprocessor(self, text |
|
57 | def writeprocessor(self, text): | |
58 | # False: the returned rawtext shouldn't be used to verify hash |
|
58 | # False: the returned rawtext shouldn't be used to verify hash | |
59 | rawtext = _extheader + text.replace(b'1', b'i') |
|
59 | rawtext = _extheader + text.replace(b'1', b'i') | |
60 | return rawtext, False |
|
60 | return rawtext, False | |
@@ -293,7 +293,7 b' def writecases(rlog, tr):' | |||||
293 |
|
293 | |||
294 | # Verify text, rawtext, and rawsize |
|
294 | # Verify text, rawtext, and rawsize | |
295 | if isext: |
|
295 | if isext: | |
296 |
rawtext = writeprocessor(None, text |
|
296 | rawtext = writeprocessor(None, text)[0] | |
297 | else: |
|
297 | else: | |
298 | rawtext = text |
|
298 | rawtext = text | |
299 | if rlog.rawsize(rev) != len(rawtext): |
|
299 | if rlog.rawsize(rev) != len(rawtext): |
@@ -40,19 +40,20 b' def wrapaddrevision(' | |||||
40 | return orig(self, text, transaction, link, p1, p2, *args, **kwargs) |
|
40 | return orig(self, text, transaction, link, p1, p2, *args, **kwargs) | |
41 |
|
41 | |||
42 |
|
42 | |||
43 | def wraprevision(orig, self, nodeorrev, *args, **kwargs): |
|
43 | def wrap_revisiondata(orig, self, nodeorrev, *args, **kwargs): | |
44 | text = orig(self, nodeorrev, *args, **kwargs) |
|
44 | text, sd = orig(self, nodeorrev, *args, **kwargs) | |
45 | if getattr(self, 'sidedatanocheck', False): |
|
45 | if getattr(self, 'sidedatanocheck', False): | |
46 | return text |
|
46 | return text, sd | |
|
47 | if self.version & 0xFFFF != 2: | |||
|
48 | return text, sd | |||
47 | if nodeorrev != nullrev and nodeorrev != nullid: |
|
49 | if nodeorrev != nullrev and nodeorrev != nullid: | |
48 | sd = self.sidedata(nodeorrev) |
|
|||
49 | if len(text) != struct.unpack('>I', sd[sidedata.SD_TEST1])[0]: |
|
50 | if len(text) != struct.unpack('>I', sd[sidedata.SD_TEST1])[0]: | |
50 | raise RuntimeError('text size mismatch') |
|
51 | raise RuntimeError('text size mismatch') | |
51 | expected = sd[sidedata.SD_TEST2] |
|
52 | expected = sd[sidedata.SD_TEST2] | |
52 | got = hashlib.sha256(text).digest() |
|
53 | got = hashlib.sha256(text).digest() | |
53 | if got != expected: |
|
54 | if got != expected: | |
54 | raise RuntimeError('sha256 mismatch') |
|
55 | raise RuntimeError('sha256 mismatch') | |
55 | return text |
|
56 | return text, sd | |
56 |
|
57 | |||
57 |
|
58 | |||
58 | def wrapgetsidedatacompanion(orig, srcrepo, dstrepo): |
|
59 | def wrapgetsidedatacompanion(orig, srcrepo, dstrepo): | |
@@ -81,7 +82,7 b' def wrapgetsidedatacompanion(orig, srcre' | |||||
81 |
|
82 | |||
82 | def extsetup(ui): |
|
83 | def extsetup(ui): | |
83 | extensions.wrapfunction(revlog.revlog, 'addrevision', wrapaddrevision) |
|
84 | extensions.wrapfunction(revlog.revlog, 'addrevision', wrapaddrevision) | |
84 | extensions.wrapfunction(revlog.revlog, 'revision', wraprevision) |
|
85 | extensions.wrapfunction(revlog.revlog, '_revisiondata', wrap_revisiondata) | |
85 | extensions.wrapfunction( |
|
86 | extensions.wrapfunction( | |
86 | upgrade_engine, 'getsidedatacompanion', wrapgetsidedatacompanion |
|
87 | upgrade_engine, 'getsidedatacompanion', wrapgetsidedatacompanion | |
87 | ) |
|
88 | ) |
General Comments 0
You need to be logged in to leave comments.
Login now