--- a/mercurial/revlog.py Tue Oct 02 17:28:54 2018 -0700
+++ b/mercurial/revlog.py Tue Oct 02 17:34:34 2018 -0700
@@ -2341,94 +2341,69 @@
destrevlog._lazydeltabase = oldlazydeltabase
destrevlog._deltabothparents = oldamd
- def censorrevision(self, node, tombstone=b''):
+ def censorrevision(self, tr, censornode, tombstone=b''):
if (self.version & 0xFFFF) == REVLOGV0:
raise error.RevlogError(_('cannot censor with version %d revlogs') %
self.version)
- rev = self.rev(node)
+ censorrev = self.rev(censornode)
tombstone = storageutil.packmeta({b'censored': tombstone}, b'')
- if len(tombstone) > self.rawsize(rev):
+ if len(tombstone) > self.rawsize(censorrev):
raise error.Abort(_('censor tombstone must be no longer than '
'censored data'))
- # Using two files instead of one makes it easy to rewrite entry-by-entry
- idxread = self.opener(self.indexfile, 'r')
- idxwrite = self.opener(self.indexfile, 'wb', atomictemp=True)
- if self.version & FLAG_INLINE_DATA:
- dataread, datawrite = idxread, idxwrite
- else:
- dataread = self.opener(self.datafile, 'r')
- datawrite = self.opener(self.datafile, 'wb', atomictemp=True)
+ # Rewriting the revlog in place is hard. Our strategy for censoring is
+ # to create a new revlog, copy all revisions to it, then replace the
+ # revlogs on transaction close.
- # Copy all revlog data up to the entry to be censored.
- offset = self.start(rev)
-
- for chunk in util.filechunkiter(idxread, limit=rev * self._io.size):
- idxwrite.write(chunk)
- for chunk in util.filechunkiter(dataread, limit=offset):
- datawrite.write(chunk)
+ newindexfile = self.indexfile + b'.tmpcensored'
+ newdatafile = self.datafile + b'.tmpcensored'
- def rewriteindex(r, newoffs, newdata=None):
- """Rewrite the index entry with a new data offset and new data.
+ # This is a bit dangerous. We could easily have a mismatch of state.
+ newrl = revlog(self.opener, newindexfile, newdatafile,
+ censorable=True)
+ newrl.version = self.version
+ newrl._generaldelta = self._generaldelta
+ newrl._io = self._io
- The newdata argument, if given, is a tuple of three positive
- integers: (new compressed, new uncompressed, added flag bits).
- """
- offlags, comp, uncomp, base, link, p1, p2, nodeid = self.index[r]
- flags = gettype(offlags)
- if newdata:
- comp, uncomp, nflags = newdata
- flags |= nflags
- offlags = offset_type(newoffs, flags)
- e = (offlags, comp, uncomp, r, link, p1, p2, nodeid)
- idxwrite.write(self._io.packentry(e, None, self.version, r))
- idxread.seek(self._io.size, 1)
+ for rev in self.revs():
+ node = self.node(rev)
+ p1, p2 = self.parents(node)
- def rewrite(r, offs, data, nflags=REVIDX_DEFAULT_FLAGS):
- """Write the given fulltext with the given data offset.
+ if rev == censorrev:
+ newrl.addrawrevision(tombstone, tr, self.linkrev(censorrev),
+ p1, p2, censornode, REVIDX_ISCENSORED)
- Returns:
- The integer number of data bytes written, for tracking data
- offsets.
- """
- flag, compdata = self.compress(data)
- newcomp = len(flag) + len(compdata)
- rewriteindex(r, offs, (newcomp, len(data), nflags))
- datawrite.write(flag)
- datawrite.write(compdata)
- dataread.seek(self.length(r), 1)
- return newcomp
-
- # Rewrite censored entry with (padded) tombstone data.
- pad = ' ' * (self.rawsize(rev) - len(tombstone))
- offset += rewrite(rev, offset, tombstone + pad, REVIDX_ISCENSORED)
+ if newrl.deltaparent(rev) != nullrev:
+ raise error.Abort(_('censored revision stored as delta; '
+ 'cannot censor'),
+ hint=_('censoring of revlogs is not '
+ 'fully implemented; please report '
+ 'this bug'))
+ continue
- # Rewrite all following filelog revisions fixing up offsets and deltas.
- for srev in pycompat.xrange(rev + 1, len(self)):
- if rev in self.parentrevs(srev):
- # Immediate children of censored node must be re-added as
- # fulltext.
- try:
- revdata = self.revision(srev)
- except error.CensoredNodeError as e:
- revdata = e.tombstone
- dlen = rewrite(srev, offset, revdata)
+ if self.iscensored(rev):
+ if self.deltaparent(rev) != nullrev:
+ raise error.Abort(_('cannot censor due to censored '
+ 'revision having delta stored'))
+ rawtext = self._chunk(rev)
else:
- # Copy any other revision data verbatim after fixing up the
- # offset.
- rewriteindex(srev, offset)
- dlen = self.length(srev)
- for chunk in util.filechunkiter(dataread, limit=dlen):
- datawrite.write(chunk)
- offset += dlen
+ rawtext = self.revision(rev, raw=True)
+
+ newrl.addrawrevision(rawtext, tr, self.linkrev(rev), p1, p2, node,
+ self.flags(rev))
- idxread.close()
- idxwrite.close()
- if dataread is not idxread:
- dataread.close()
- datawrite.close()
+ tr.addbackup(self.indexfile, location='store')
+ if not self._inline:
+ tr.addbackup(self.datafile, location='store')
+
+ self.opener.rename(newrl.indexfile, self.indexfile)
+ if not self._inline:
+ self.opener.rename(newrl.datafile, self.datafile)
+
+ self.clearcaches()
+ self._loadindex(self.version, None)
def verifyintegrity(self, state):
"""Verifies the integrity of the revlog.