##// END OF EJS Templates
revlog: rewrite censoring logic...
Gregory Szorc -
r40092:324b4b10 default
parent child Browse files
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 # fulltext.
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 dlen = self.length(srev)
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