Show More
@@ -101,7 +101,7 b' class filelog(object):' | |||||
101 | return self._revlog.strip(minlink, transaction) |
|
101 | return self._revlog.strip(minlink, transaction) | |
102 |
|
102 | |||
103 | def censorrevision(self, tr, node, tombstone=b''): |
|
103 | def censorrevision(self, tr, node, tombstone=b''): | |
104 | return self._revlog.censorrevision(node, tombstone=tombstone) |
|
104 | return self._revlog.censorrevision(tr, node, tombstone=tombstone) | |
105 |
|
105 | |||
106 | def files(self): |
|
106 | def files(self): | |
107 | return self._revlog.files() |
|
107 | return self._revlog.files() |
@@ -1682,7 +1682,7 b' class localrepository(object):' | |||||
1682 | rp = report |
|
1682 | rp = report | |
1683 | else: |
|
1683 | else: | |
1684 | rp = self.ui.warn |
|
1684 | rp = self.ui.warn | |
1685 | vfsmap = {'plain': self.vfs} # root of .hg/ |
|
1685 | vfsmap = {'plain': self.vfs, 'store': self.svfs} # root of .hg/ | |
1686 | # we must avoid cyclic reference between repo and transaction. |
|
1686 | # we must avoid cyclic reference between repo and transaction. | |
1687 | reporef = weakref.ref(self) |
|
1687 | reporef = weakref.ref(self) | |
1688 | # Code to track tag movement |
|
1688 | # Code to track tag movement |
@@ -2341,94 +2341,69 b' class revlog(object):' | |||||
2341 | destrevlog._lazydeltabase = oldlazydeltabase |
|
2341 | destrevlog._lazydeltabase = oldlazydeltabase | |
2342 | destrevlog._deltabothparents = oldamd |
|
2342 | destrevlog._deltabothparents = oldamd | |
2343 |
|
2343 | |||
2344 | def censorrevision(self, node, tombstone=b''): |
|
2344 | def censorrevision(self, tr, censornode, tombstone=b''): | |
2345 | if (self.version & 0xFFFF) == REVLOGV0: |
|
2345 | if (self.version & 0xFFFF) == REVLOGV0: | |
2346 | raise error.RevlogError(_('cannot censor with version %d revlogs') % |
|
2346 | raise error.RevlogError(_('cannot censor with version %d revlogs') % | |
2347 | self.version) |
|
2347 | self.version) | |
2348 |
|
2348 | |||
2349 | rev = self.rev(node) |
|
2349 | censorrev = self.rev(censornode) | |
2350 | tombstone = storageutil.packmeta({b'censored': tombstone}, b'') |
|
2350 | tombstone = storageutil.packmeta({b'censored': tombstone}, b'') | |
2351 |
|
2351 | |||
2352 | if len(tombstone) > self.rawsize(rev): |
|
2352 | if len(tombstone) > self.rawsize(censorrev): | |
2353 | raise error.Abort(_('censor tombstone must be no longer than ' |
|
2353 | raise error.Abort(_('censor tombstone must be no longer than ' | |
2354 | 'censored data')) |
|
2354 | 'censored data')) | |
2355 |
|
2355 | |||
2356 | # Using two files instead of one makes it easy to rewrite entry-by-entry |
|
2356 | # Rewriting the revlog in place is hard. Our strategy for censoring is | |
2357 | idxread = self.opener(self.indexfile, 'r') |
|
2357 | # to create a new revlog, copy all revisions to it, then replace the | |
2358 | idxwrite = self.opener(self.indexfile, 'wb', atomictemp=True) |
|
2358 | # revlogs on transaction close. | |
2359 | if self.version & FLAG_INLINE_DATA: |
|
|||
2360 | dataread, datawrite = idxread, idxwrite |
|
|||
2361 | else: |
|
|||
2362 | dataread = self.opener(self.datafile, 'r') |
|
|||
2363 | datawrite = self.opener(self.datafile, 'wb', atomictemp=True) |
|
|||
2364 |
|
2359 | |||
2365 | # Copy all revlog data up to the entry to be censored. |
|
2360 | newindexfile = self.indexfile + b'.tmpcensored' | |
2366 | offset = self.start(rev) |
|
2361 | newdatafile = self.datafile + b'.tmpcensored' | |
2367 |
|
||||
2368 | for chunk in util.filechunkiter(idxread, limit=rev * self._io.size): |
|
|||
2369 | idxwrite.write(chunk) |
|
|||
2370 | for chunk in util.filechunkiter(dataread, limit=offset): |
|
|||
2371 | datawrite.write(chunk) |
|
|||
2372 |
|
2362 | |||
2373 | def rewriteindex(r, newoffs, newdata=None): |
|
2363 | # This is a bit dangerous. We could easily have a mismatch of state. | |
2374 | """Rewrite the index entry with a new data offset and new data. |
|
2364 | newrl = revlog(self.opener, newindexfile, newdatafile, | |
|
2365 | censorable=True) | |||
|
2366 | newrl.version = self.version | |||
|
2367 | newrl._generaldelta = self._generaldelta | |||
|
2368 | newrl._io = self._io | |||
2375 |
|
2369 | |||
2376 | The newdata argument, if given, is a tuple of three positive |
|
2370 | for rev in self.revs(): | |
2377 | integers: (new compressed, new uncompressed, added flag bits). |
|
2371 | node = self.node(rev) | |
2378 | """ |
|
2372 | p1, p2 = self.parents(node) | |
2379 | offlags, comp, uncomp, base, link, p1, p2, nodeid = self.index[r] |
|
|||
2380 | flags = gettype(offlags) |
|
|||
2381 | if newdata: |
|
|||
2382 | comp, uncomp, nflags = newdata |
|
|||
2383 | flags |= nflags |
|
|||
2384 | offlags = offset_type(newoffs, flags) |
|
|||
2385 | e = (offlags, comp, uncomp, r, link, p1, p2, nodeid) |
|
|||
2386 | idxwrite.write(self._io.packentry(e, None, self.version, r)) |
|
|||
2387 | idxread.seek(self._io.size, 1) |
|
|||
2388 |
|
2373 | |||
2389 | def rewrite(r, offs, data, nflags=REVIDX_DEFAULT_FLAGS): |
|
2374 | if rev == censorrev: | |
2390 | """Write the given fulltext with the given data offset. |
|
2375 | newrl.addrawrevision(tombstone, tr, self.linkrev(censorrev), | |
|
2376 | p1, p2, censornode, REVIDX_ISCENSORED) | |||
2391 |
|
2377 | |||
2392 | Returns: |
|
2378 | if newrl.deltaparent(rev) != nullrev: | |
2393 | The integer number of data bytes written, for tracking data |
|
2379 | raise error.Abort(_('censored revision stored as delta; ' | |
2394 | offsets. |
|
2380 | 'cannot censor'), | |
2395 | """ |
|
2381 | hint=_('censoring of revlogs is not ' | |
2396 | flag, compdata = self.compress(data) |
|
2382 | 'fully implemented; please report ' | |
2397 | newcomp = len(flag) + len(compdata) |
|
2383 | 'this bug')) | |
2398 | rewriteindex(r, offs, (newcomp, len(data), nflags)) |
|
2384 | continue | |
2399 | datawrite.write(flag) |
|
|||
2400 | datawrite.write(compdata) |
|
|||
2401 | dataread.seek(self.length(r), 1) |
|
|||
2402 | return newcomp |
|
|||
2403 |
|
||||
2404 | # Rewrite censored entry with (padded) tombstone data. |
|
|||
2405 | pad = ' ' * (self.rawsize(rev) - len(tombstone)) |
|
|||
2406 | offset += rewrite(rev, offset, tombstone + pad, REVIDX_ISCENSORED) |
|
|||
2407 |
|
2385 | |||
2408 | # Rewrite all following filelog revisions fixing up offsets and deltas. |
|
2386 | if self.iscensored(rev): | |
2409 | for srev in pycompat.xrange(rev + 1, len(self)): |
|
2387 | if self.deltaparent(rev) != nullrev: | |
2410 | if rev in self.parentrevs(srev): |
|
2388 | raise error.Abort(_('cannot censor due to censored ' | |
2411 | # Immediate children of censored node must be re-added as |
|
2389 | 'revision having delta stored')) | |
2412 |
|
|
2390 | rawtext = self._chunk(rev) | |
2413 | try: |
|
|||
2414 | revdata = self.revision(srev) |
|
|||
2415 | except error.CensoredNodeError as e: |
|
|||
2416 | revdata = e.tombstone |
|
|||
2417 | dlen = rewrite(srev, offset, revdata) |
|
|||
2418 | else: |
|
2391 | else: | |
2419 | # Copy any other revision data verbatim after fixing up the |
|
2392 | rawtext = self.revision(rev, raw=True) | |
2420 | # offset. |
|
2393 | ||
2421 | rewriteindex(srev, offset) |
|
2394 | newrl.addrawrevision(rawtext, tr, self.linkrev(rev), p1, p2, node, | |
2422 |
|
|
2395 | self.flags(rev)) | |
2423 | for chunk in util.filechunkiter(dataread, limit=dlen): |
|
|||
2424 | datawrite.write(chunk) |
|
|||
2425 | offset += dlen |
|
|||
2426 |
|
2396 | |||
2427 | idxread.close() |
|
2397 | tr.addbackup(self.indexfile, location='store') | |
2428 | idxwrite.close() |
|
2398 | if not self._inline: | |
2429 | if dataread is not idxread: |
|
2399 | tr.addbackup(self.datafile, location='store') | |
2430 | dataread.close() |
|
2400 | ||
2431 | datawrite.close() |
|
2401 | self.opener.rename(newrl.indexfile, self.indexfile) | |
|
2402 | if not self._inline: | |||
|
2403 | self.opener.rename(newrl.datafile, self.datafile) | |||
|
2404 | ||||
|
2405 | self.clearcaches() | |||
|
2406 | self._loadindex(self.version, None) | |||
2432 |
|
2407 | |||
2433 | def verifyintegrity(self, state): |
|
2408 | def verifyintegrity(self, state): | |
2434 | """Verifies the integrity of the revlog. |
|
2409 | """Verifies the integrity of the revlog. |
@@ -1175,14 +1175,9 b' class ifilemutationtests(basetestcase):' | |||||
1175 | self.assertEqual(list(f.revs()), [0, 1, 2]) |
|
1175 | self.assertEqual(list(f.revs()), [0, 1, 2]) | |
1176 |
|
1176 | |||
1177 | self.assertEqual(f.read(node0), b'foo\n' * 30) |
|
1177 | self.assertEqual(f.read(node0), b'foo\n' * 30) | |
|
1178 | self.assertEqual(f.read(node2), b'foo\n' * 32) | |||
1178 |
|
1179 | |||
1179 | # TODO revlog can't resolve revision after censor. Probably due to a |
|
1180 | with self.assertRaises(error.CensoredNodeError): | |
1180 | # cache on the revlog instance. |
|
|||
1181 | with self.assertRaises(error.StorageError): |
|
|||
1182 | self.assertEqual(f.read(node2), b'foo\n' * 32) |
|
|||
1183 |
|
||||
1184 | # TODO should raise CensoredNodeError, but fallout from above prevents. |
|
|||
1185 | with self.assertRaises(error.StorageError): |
|
|||
1186 | f.read(node1) |
|
1181 | f.read(node1) | |
1187 |
|
1182 | |||
1188 | def testgetstrippointnoparents(self): |
|
1183 | def testgetstrippointnoparents(self): |
@@ -30,7 +30,7 b' def makefilefn(self):' | |||||
30 | return fl |
|
30 | return fl | |
31 |
|
31 | |||
32 | def maketransaction(self): |
|
32 | def maketransaction(self): | |
33 | vfsmap = {'plain': STATE['vfs']} |
|
33 | vfsmap = {'plain': STATE['vfs'], 'store': STATE['vfs']} | |
34 |
|
34 | |||
35 | return transaction.transaction(STATE['ui'].warn, STATE['vfs'], vfsmap, |
|
35 | return transaction.transaction(STATE['ui'].warn, STATE['vfs'], vfsmap, | |
36 | b'journal', b'undo') |
|
36 | b'journal', b'undo') |
General Comments 0
You need to be logged in to leave comments.
Login now