##// END OF EJS Templates
censor: migrate the logic to a set of `censor_revs`...
marmoute -
r48264:c81a5297 default
parent child Browse files
Show More
@@ -1,461 +1,465 b''
1 # censor code related to censoring revision
1 # censor code related to censoring revision
2 # coding: utf8
2 # coding: utf8
3 #
3 #
4 # Copyright 2021 Pierre-Yves David <pierre-yves.david@octobus.net>
4 # Copyright 2021 Pierre-Yves David <pierre-yves.david@octobus.net>
5 # Copyright 2015 Google, Inc <martinvonz@google.com>
5 # Copyright 2015 Google, Inc <martinvonz@google.com>
6 #
6 #
7 # This software may be used and distributed according to the terms of the
7 # This software may be used and distributed according to the terms of the
8 # GNU General Public License version 2 or any later version.
8 # GNU General Public License version 2 or any later version.
9
9
10 import contextlib
10 import contextlib
11 import os
11 import os
12
12
13 from ..node import (
13 from ..node import (
14 nullrev,
14 nullrev,
15 )
15 )
16 from .constants import (
16 from .constants import (
17 COMP_MODE_PLAIN,
17 COMP_MODE_PLAIN,
18 ENTRY_DATA_COMPRESSED_LENGTH,
18 ENTRY_DATA_COMPRESSED_LENGTH,
19 ENTRY_DATA_COMPRESSION_MODE,
19 ENTRY_DATA_COMPRESSION_MODE,
20 ENTRY_DATA_OFFSET,
20 ENTRY_DATA_OFFSET,
21 ENTRY_DATA_UNCOMPRESSED_LENGTH,
21 ENTRY_DATA_UNCOMPRESSED_LENGTH,
22 ENTRY_DELTA_BASE,
22 ENTRY_DELTA_BASE,
23 ENTRY_LINK_REV,
23 ENTRY_LINK_REV,
24 ENTRY_NODE_ID,
24 ENTRY_NODE_ID,
25 ENTRY_PARENT_1,
25 ENTRY_PARENT_1,
26 ENTRY_PARENT_2,
26 ENTRY_PARENT_2,
27 ENTRY_SIDEDATA_COMPRESSED_LENGTH,
27 ENTRY_SIDEDATA_COMPRESSED_LENGTH,
28 ENTRY_SIDEDATA_COMPRESSION_MODE,
28 ENTRY_SIDEDATA_COMPRESSION_MODE,
29 ENTRY_SIDEDATA_OFFSET,
29 ENTRY_SIDEDATA_OFFSET,
30 REVLOGV0,
30 REVLOGV0,
31 REVLOGV1,
31 REVLOGV1,
32 )
32 )
33 from ..i18n import _
33 from ..i18n import _
34
34
35 from .. import (
35 from .. import (
36 error,
36 error,
37 pycompat,
37 pycompat,
38 revlogutils,
38 revlogutils,
39 util,
39 util,
40 )
40 )
41 from ..utils import (
41 from ..utils import (
42 storageutil,
42 storageutil,
43 )
43 )
44 from . import (
44 from . import (
45 constants,
45 constants,
46 deltas,
46 deltas,
47 )
47 )
48
48
49
49
50 def v1_censor(rl, tr, censornode, tombstone=b''):
50 def v1_censor(rl, tr, censornode, tombstone=b''):
51 """censors a revision in a "version 1" revlog"""
51 """censors a revision in a "version 1" revlog"""
52 assert rl._format_version == constants.REVLOGV1, rl._format_version
52 assert rl._format_version == constants.REVLOGV1, rl._format_version
53
53
54 # avoid cycle
54 # avoid cycle
55 from .. import revlog
55 from .. import revlog
56
56
57 censorrev = rl.rev(censornode)
57 censorrev = rl.rev(censornode)
58 tombstone = storageutil.packmeta({b'censored': tombstone}, b'')
58 tombstone = storageutil.packmeta({b'censored': tombstone}, b'')
59
59
60 # Rewriting the revlog in place is hard. Our strategy for censoring is
60 # Rewriting the revlog in place is hard. Our strategy for censoring is
61 # to create a new revlog, copy all revisions to it, then replace the
61 # to create a new revlog, copy all revisions to it, then replace the
62 # revlogs on transaction close.
62 # revlogs on transaction close.
63 #
63 #
64 # This is a bit dangerous. We could easily have a mismatch of state.
64 # This is a bit dangerous. We could easily have a mismatch of state.
65 newrl = revlog.revlog(
65 newrl = revlog.revlog(
66 rl.opener,
66 rl.opener,
67 target=rl.target,
67 target=rl.target,
68 radix=rl.radix,
68 radix=rl.radix,
69 postfix=b'tmpcensored',
69 postfix=b'tmpcensored',
70 censorable=True,
70 censorable=True,
71 )
71 )
72 newrl._format_version = rl._format_version
72 newrl._format_version = rl._format_version
73 newrl._format_flags = rl._format_flags
73 newrl._format_flags = rl._format_flags
74 newrl._generaldelta = rl._generaldelta
74 newrl._generaldelta = rl._generaldelta
75 newrl._parse_index = rl._parse_index
75 newrl._parse_index = rl._parse_index
76
76
77 for rev in rl.revs():
77 for rev in rl.revs():
78 node = rl.node(rev)
78 node = rl.node(rev)
79 p1, p2 = rl.parents(node)
79 p1, p2 = rl.parents(node)
80
80
81 if rev == censorrev:
81 if rev == censorrev:
82 newrl.addrawrevision(
82 newrl.addrawrevision(
83 tombstone,
83 tombstone,
84 tr,
84 tr,
85 rl.linkrev(censorrev),
85 rl.linkrev(censorrev),
86 p1,
86 p1,
87 p2,
87 p2,
88 censornode,
88 censornode,
89 constants.REVIDX_ISCENSORED,
89 constants.REVIDX_ISCENSORED,
90 )
90 )
91
91
92 if newrl.deltaparent(rev) != nullrev:
92 if newrl.deltaparent(rev) != nullrev:
93 m = _(b'censored revision stored as delta; cannot censor')
93 m = _(b'censored revision stored as delta; cannot censor')
94 h = _(
94 h = _(
95 b'censoring of revlogs is not fully implemented;'
95 b'censoring of revlogs is not fully implemented;'
96 b' please report this bug'
96 b' please report this bug'
97 )
97 )
98 raise error.Abort(m, hint=h)
98 raise error.Abort(m, hint=h)
99 continue
99 continue
100
100
101 if rl.iscensored(rev):
101 if rl.iscensored(rev):
102 if rl.deltaparent(rev) != nullrev:
102 if rl.deltaparent(rev) != nullrev:
103 m = _(
103 m = _(
104 b'cannot censor due to censored '
104 b'cannot censor due to censored '
105 b'revision having delta stored'
105 b'revision having delta stored'
106 )
106 )
107 raise error.Abort(m)
107 raise error.Abort(m)
108 rawtext = rl._chunk(rev)
108 rawtext = rl._chunk(rev)
109 else:
109 else:
110 rawtext = rl.rawdata(rev)
110 rawtext = rl.rawdata(rev)
111
111
112 newrl.addrawrevision(
112 newrl.addrawrevision(
113 rawtext, tr, rl.linkrev(rev), p1, p2, node, rl.flags(rev)
113 rawtext, tr, rl.linkrev(rev), p1, p2, node, rl.flags(rev)
114 )
114 )
115
115
116 tr.addbackup(rl._indexfile, location=b'store')
116 tr.addbackup(rl._indexfile, location=b'store')
117 if not rl._inline:
117 if not rl._inline:
118 tr.addbackup(rl._datafile, location=b'store')
118 tr.addbackup(rl._datafile, location=b'store')
119
119
120 rl.opener.rename(newrl._indexfile, rl._indexfile)
120 rl.opener.rename(newrl._indexfile, rl._indexfile)
121 if not rl._inline:
121 if not rl._inline:
122 rl.opener.rename(newrl._datafile, rl._datafile)
122 rl.opener.rename(newrl._datafile, rl._datafile)
123
123
124 rl.clearcaches()
124 rl.clearcaches()
125 rl._loadindex()
125 rl._loadindex()
126
126
127
127
128 def v2_censor(revlog, tr, censornode, tombstone=b''):
128 def v2_censor(revlog, tr, censornode, tombstone=b''):
129 """censors a revision in a "version 2" revlog"""
129 """censors a revision in a "version 2" revlog"""
130 # General principle
130 # General principle
131 #
131 #
132 # We create new revlog files (index/data/sidedata) to copy the content of
132 # We create new revlog files (index/data/sidedata) to copy the content of
133 # the existing data without the censored data.
133 # the existing data without the censored data.
134 #
134 #
135 # We need to recompute new delta for any revision that used the censored
135 # We need to recompute new delta for any revision that used the censored
136 # revision as delta base. As the cumulative size of the new delta may be
136 # revision as delta base. As the cumulative size of the new delta may be
137 # large, we store them in a temporary file until they are stored in their
137 # large, we store them in a temporary file until they are stored in their
138 # final destination.
138 # final destination.
139 #
139 #
140 # All data before the censored data can be blindly copied. The rest needs
140 # All data before the censored data can be blindly copied. The rest needs
141 # to be copied as we go and the associated index entry needs adjustement.
141 # to be copied as we go and the associated index entry needs adjustement.
142
142
143 assert revlog._format_version != REVLOGV0, revlog._format_version
143 assert revlog._format_version != REVLOGV0, revlog._format_version
144 assert revlog._format_version != REVLOGV1, revlog._format_version
144 assert revlog._format_version != REVLOGV1, revlog._format_version
145
145
146 old_index = revlog.index
146 old_index = revlog.index
147 docket = revlog._docket
147 docket = revlog._docket
148
148
149 censor_rev = revlog.rev(censornode)
149 censor_revs = {revlog.rev(censornode)}
150 tombstone = storageutil.packmeta({b'censored': tombstone}, b'')
150 tombstone = storageutil.packmeta({b'censored': tombstone}, b'')
151
151
152 censored_entry = revlog.index[censor_rev]
152 first_excl_rev = min(censor_revs)
153 index_cutoff = revlog.index.entry_size * censor_rev
153
154 data_cutoff = censored_entry[ENTRY_DATA_OFFSET] >> 16
154 first_excl_entry = revlog.index[first_excl_rev]
155 sidedata_cutoff = revlog.sidedata_cut_off(censor_rev)
155 index_cutoff = revlog.index.entry_size * first_excl_rev
156 data_cutoff = first_excl_entry[ENTRY_DATA_OFFSET] >> 16
157 sidedata_cutoff = revlog.sidedata_cut_off(first_excl_rev)
156
158
157 with pycompat.unnamedtempfile(mode=b"w+b") as tmp_storage:
159 with pycompat.unnamedtempfile(mode=b"w+b") as tmp_storage:
158 # rev β†’ (new_base, data_start, data_end, compression_mode)
160 # rev β†’ (new_base, data_start, data_end, compression_mode)
159 rewritten_entries = _precompute_rewritten_delta(
161 rewritten_entries = _precompute_rewritten_delta(
160 revlog,
162 revlog,
161 old_index,
163 old_index,
162 {censor_rev},
164 censor_revs,
163 tmp_storage,
165 tmp_storage,
164 )
166 )
165
167
166 all_files = _setup_new_files(
168 all_files = _setup_new_files(
167 revlog,
169 revlog,
168 index_cutoff,
170 index_cutoff,
169 data_cutoff,
171 data_cutoff,
170 sidedata_cutoff,
172 sidedata_cutoff,
171 )
173 )
172
174
173 # we dont need to open the old index file since its content already
175 # we dont need to open the old index file since its content already
174 # exist in a usable form in `old_index`.
176 # exist in a usable form in `old_index`.
175 with all_files() as open_files:
177 with all_files() as open_files:
176 (
178 (
177 old_data_file,
179 old_data_file,
178 old_sidedata_file,
180 old_sidedata_file,
179 new_index_file,
181 new_index_file,
180 new_data_file,
182 new_data_file,
181 new_sidedata_file,
183 new_sidedata_file,
182 ) = open_files
184 ) = open_files
183
185
184 # writing the censored revision
186 # writing the censored revision
187
188 # Writing all subsequent revisions
189 for rev in range(first_excl_rev, len(old_index)):
190 if rev in censor_revs:
185 _rewrite_censor(
191 _rewrite_censor(
186 revlog,
192 revlog,
187 old_index,
193 old_index,
188 open_files,
194 open_files,
189 censor_rev,
195 rev,
190 tombstone,
196 tombstone,
191 )
197 )
192
198 else:
193 # Writing all subsequent revisions
194 for rev in range(censor_rev + 1, len(old_index)):
195 _rewrite_simple(
199 _rewrite_simple(
196 revlog,
200 revlog,
197 old_index,
201 old_index,
198 open_files,
202 open_files,
199 rev,
203 rev,
200 rewritten_entries,
204 rewritten_entries,
201 tmp_storage,
205 tmp_storage,
202 )
206 )
203 docket.write(transaction=None, stripping=True)
207 docket.write(transaction=None, stripping=True)
204
208
205
209
206 def _precompute_rewritten_delta(
210 def _precompute_rewritten_delta(
207 revlog,
211 revlog,
208 old_index,
212 old_index,
209 excluded_revs,
213 excluded_revs,
210 tmp_storage,
214 tmp_storage,
211 ):
215 ):
212 """Compute new delta for revisions whose delta is based on revision that
216 """Compute new delta for revisions whose delta is based on revision that
213 will not survive as is.
217 will not survive as is.
214
218
215 Return a mapping: {rev β†’ (new_base, data_start, data_end, compression_mode)}
219 Return a mapping: {rev β†’ (new_base, data_start, data_end, compression_mode)}
216 """
220 """
217 dc = deltas.deltacomputer(revlog)
221 dc = deltas.deltacomputer(revlog)
218 rewritten_entries = {}
222 rewritten_entries = {}
219 first_excl_rev = min(excluded_revs)
223 first_excl_rev = min(excluded_revs)
220 with revlog._segmentfile._open_read() as dfh:
224 with revlog._segmentfile._open_read() as dfh:
221 for rev in range(first_excl_rev, len(old_index)):
225 for rev in range(first_excl_rev, len(old_index)):
222 if rev in excluded_revs:
226 if rev in excluded_revs:
223 # this revision will be preserved as is, so we don't need to
227 # this revision will be preserved as is, so we don't need to
224 # consider recomputing a delta.
228 # consider recomputing a delta.
225 continue
229 continue
226 entry = old_index[rev]
230 entry = old_index[rev]
227 if entry[ENTRY_DELTA_BASE] not in excluded_revs:
231 if entry[ENTRY_DELTA_BASE] not in excluded_revs:
228 continue
232 continue
229 # This is a revision that use the censored revision as the base
233 # This is a revision that use the censored revision as the base
230 # for its delta. We need a need new deltas
234 # for its delta. We need a need new deltas
231 if entry[ENTRY_DATA_UNCOMPRESSED_LENGTH] == 0:
235 if entry[ENTRY_DATA_UNCOMPRESSED_LENGTH] == 0:
232 # this revision is empty, we can delta against nullrev
236 # this revision is empty, we can delta against nullrev
233 rewritten_entries[rev] = (nullrev, 0, 0, COMP_MODE_PLAIN)
237 rewritten_entries[rev] = (nullrev, 0, 0, COMP_MODE_PLAIN)
234 else:
238 else:
235
239
236 text = revlog.rawdata(rev, _df=dfh)
240 text = revlog.rawdata(rev, _df=dfh)
237 info = revlogutils.revisioninfo(
241 info = revlogutils.revisioninfo(
238 node=entry[ENTRY_NODE_ID],
242 node=entry[ENTRY_NODE_ID],
239 p1=revlog.node(entry[ENTRY_PARENT_1]),
243 p1=revlog.node(entry[ENTRY_PARENT_1]),
240 p2=revlog.node(entry[ENTRY_PARENT_2]),
244 p2=revlog.node(entry[ENTRY_PARENT_2]),
241 btext=[text],
245 btext=[text],
242 textlen=len(text),
246 textlen=len(text),
243 cachedelta=None,
247 cachedelta=None,
244 flags=entry[ENTRY_DATA_OFFSET] & 0xFFFF,
248 flags=entry[ENTRY_DATA_OFFSET] & 0xFFFF,
245 )
249 )
246 d = dc.finddeltainfo(
250 d = dc.finddeltainfo(
247 info, dfh, excluded_bases=excluded_revs, target_rev=rev
251 info, dfh, excluded_bases=excluded_revs, target_rev=rev
248 )
252 )
249 default_comp = revlog._docket.default_compression_header
253 default_comp = revlog._docket.default_compression_header
250 comp_mode, d = deltas.delta_compression(default_comp, d)
254 comp_mode, d = deltas.delta_compression(default_comp, d)
251 # using `tell` is a bit lazy, but we are not here for speed
255 # using `tell` is a bit lazy, but we are not here for speed
252 start = tmp_storage.tell()
256 start = tmp_storage.tell()
253 tmp_storage.write(d.data[1])
257 tmp_storage.write(d.data[1])
254 end = tmp_storage.tell()
258 end = tmp_storage.tell()
255 rewritten_entries[rev] = (d.base, start, end, comp_mode)
259 rewritten_entries[rev] = (d.base, start, end, comp_mode)
256 return rewritten_entries
260 return rewritten_entries
257
261
258
262
259 def _setup_new_files(
263 def _setup_new_files(
260 revlog,
264 revlog,
261 index_cutoff,
265 index_cutoff,
262 data_cutoff,
266 data_cutoff,
263 sidedata_cutoff,
267 sidedata_cutoff,
264 ):
268 ):
265 """
269 """
266
270
267 return a context manager to open all the relevant files:
271 return a context manager to open all the relevant files:
268 - old_data_file,
272 - old_data_file,
269 - old_sidedata_file,
273 - old_sidedata_file,
270 - new_index_file,
274 - new_index_file,
271 - new_data_file,
275 - new_data_file,
272 - new_sidedata_file,
276 - new_sidedata_file,
273
277
274 The old_index_file is not here because it is accessed through the
278 The old_index_file is not here because it is accessed through the
275 `old_index` object if the caller function.
279 `old_index` object if the caller function.
276 """
280 """
277 docket = revlog._docket
281 docket = revlog._docket
278 old_index_filepath = revlog.opener.join(docket.index_filepath())
282 old_index_filepath = revlog.opener.join(docket.index_filepath())
279 old_data_filepath = revlog.opener.join(docket.data_filepath())
283 old_data_filepath = revlog.opener.join(docket.data_filepath())
280 old_sidedata_filepath = revlog.opener.join(docket.sidedata_filepath())
284 old_sidedata_filepath = revlog.opener.join(docket.sidedata_filepath())
281
285
282 new_index_filepath = revlog.opener.join(docket.new_index_file())
286 new_index_filepath = revlog.opener.join(docket.new_index_file())
283 new_data_filepath = revlog.opener.join(docket.new_data_file())
287 new_data_filepath = revlog.opener.join(docket.new_data_file())
284 new_sidedata_filepath = revlog.opener.join(docket.new_sidedata_file())
288 new_sidedata_filepath = revlog.opener.join(docket.new_sidedata_file())
285
289
286 util.copyfile(old_index_filepath, new_index_filepath, nb_bytes=index_cutoff)
290 util.copyfile(old_index_filepath, new_index_filepath, nb_bytes=index_cutoff)
287 util.copyfile(old_data_filepath, new_data_filepath, nb_bytes=data_cutoff)
291 util.copyfile(old_data_filepath, new_data_filepath, nb_bytes=data_cutoff)
288 util.copyfile(
292 util.copyfile(
289 old_sidedata_filepath,
293 old_sidedata_filepath,
290 new_sidedata_filepath,
294 new_sidedata_filepath,
291 nb_bytes=sidedata_cutoff,
295 nb_bytes=sidedata_cutoff,
292 )
296 )
293 revlog.opener.register_file(docket.index_filepath())
297 revlog.opener.register_file(docket.index_filepath())
294 revlog.opener.register_file(docket.data_filepath())
298 revlog.opener.register_file(docket.data_filepath())
295 revlog.opener.register_file(docket.sidedata_filepath())
299 revlog.opener.register_file(docket.sidedata_filepath())
296
300
297 docket.index_end = index_cutoff
301 docket.index_end = index_cutoff
298 docket.data_end = data_cutoff
302 docket.data_end = data_cutoff
299 docket.sidedata_end = sidedata_cutoff
303 docket.sidedata_end = sidedata_cutoff
300
304
301 # reload the revlog internal information
305 # reload the revlog internal information
302 revlog.clearcaches()
306 revlog.clearcaches()
303 revlog._loadindex(docket=docket)
307 revlog._loadindex(docket=docket)
304
308
305 @contextlib.contextmanager
309 @contextlib.contextmanager
306 def all_files_opener():
310 def all_files_opener():
307 # hide opening in an helper function to please check-code, black
311 # hide opening in an helper function to please check-code, black
308 # and various python version at the same time
312 # and various python version at the same time
309 with open(old_data_filepath, 'rb') as old_data_file:
313 with open(old_data_filepath, 'rb') as old_data_file:
310 with open(old_sidedata_filepath, 'rb') as old_sidedata_file:
314 with open(old_sidedata_filepath, 'rb') as old_sidedata_file:
311 with open(new_index_filepath, 'r+b') as new_index_file:
315 with open(new_index_filepath, 'r+b') as new_index_file:
312 with open(new_data_filepath, 'r+b') as new_data_file:
316 with open(new_data_filepath, 'r+b') as new_data_file:
313 with open(
317 with open(
314 new_sidedata_filepath, 'r+b'
318 new_sidedata_filepath, 'r+b'
315 ) as new_sidedata_file:
319 ) as new_sidedata_file:
316 new_index_file.seek(0, os.SEEK_END)
320 new_index_file.seek(0, os.SEEK_END)
317 assert new_index_file.tell() == index_cutoff
321 assert new_index_file.tell() == index_cutoff
318 new_data_file.seek(0, os.SEEK_END)
322 new_data_file.seek(0, os.SEEK_END)
319 assert new_data_file.tell() == data_cutoff
323 assert new_data_file.tell() == data_cutoff
320 new_sidedata_file.seek(0, os.SEEK_END)
324 new_sidedata_file.seek(0, os.SEEK_END)
321 assert new_sidedata_file.tell() == sidedata_cutoff
325 assert new_sidedata_file.tell() == sidedata_cutoff
322 yield (
326 yield (
323 old_data_file,
327 old_data_file,
324 old_sidedata_file,
328 old_sidedata_file,
325 new_index_file,
329 new_index_file,
326 new_data_file,
330 new_data_file,
327 new_sidedata_file,
331 new_sidedata_file,
328 )
332 )
329
333
330 return all_files_opener
334 return all_files_opener
331
335
332
336
333 def _rewrite_simple(
337 def _rewrite_simple(
334 revlog,
338 revlog,
335 old_index,
339 old_index,
336 all_files,
340 all_files,
337 rev,
341 rev,
338 rewritten_entries,
342 rewritten_entries,
339 tmp_storage,
343 tmp_storage,
340 ):
344 ):
341 """append a normal revision to the index after the rewritten one(s)"""
345 """append a normal revision to the index after the rewritten one(s)"""
342 (
346 (
343 old_data_file,
347 old_data_file,
344 old_sidedata_file,
348 old_sidedata_file,
345 new_index_file,
349 new_index_file,
346 new_data_file,
350 new_data_file,
347 new_sidedata_file,
351 new_sidedata_file,
348 ) = all_files
352 ) = all_files
349 entry = old_index[rev]
353 entry = old_index[rev]
350 flags = entry[ENTRY_DATA_OFFSET] & 0xFFFF
354 flags = entry[ENTRY_DATA_OFFSET] & 0xFFFF
351 old_data_offset = entry[ENTRY_DATA_OFFSET] >> 16
355 old_data_offset = entry[ENTRY_DATA_OFFSET] >> 16
352
356
353 if rev not in rewritten_entries:
357 if rev not in rewritten_entries:
354 old_data_file.seek(old_data_offset)
358 old_data_file.seek(old_data_offset)
355 new_data_size = entry[ENTRY_DATA_COMPRESSED_LENGTH]
359 new_data_size = entry[ENTRY_DATA_COMPRESSED_LENGTH]
356 new_data = old_data_file.read(new_data_size)
360 new_data = old_data_file.read(new_data_size)
357 data_delta_base = entry[ENTRY_DELTA_BASE]
361 data_delta_base = entry[ENTRY_DELTA_BASE]
358 d_comp_mode = entry[ENTRY_DATA_COMPRESSION_MODE]
362 d_comp_mode = entry[ENTRY_DATA_COMPRESSION_MODE]
359 else:
363 else:
360 (
364 (
361 data_delta_base,
365 data_delta_base,
362 start,
366 start,
363 end,
367 end,
364 d_comp_mode,
368 d_comp_mode,
365 ) = rewritten_entries[rev]
369 ) = rewritten_entries[rev]
366 new_data_size = end - start
370 new_data_size = end - start
367 tmp_storage.seek(start)
371 tmp_storage.seek(start)
368 new_data = tmp_storage.read(new_data_size)
372 new_data = tmp_storage.read(new_data_size)
369
373
370 # It might be faster to group continuous read/write operation,
374 # It might be faster to group continuous read/write operation,
371 # however, this is censor, an operation that is not focussed
375 # however, this is censor, an operation that is not focussed
372 # around stellar performance. So I have not written this
376 # around stellar performance. So I have not written this
373 # optimisation yet.
377 # optimisation yet.
374 new_data_offset = new_data_file.tell()
378 new_data_offset = new_data_file.tell()
375 new_data_file.write(new_data)
379 new_data_file.write(new_data)
376
380
377 sidedata_size = entry[ENTRY_SIDEDATA_COMPRESSED_LENGTH]
381 sidedata_size = entry[ENTRY_SIDEDATA_COMPRESSED_LENGTH]
378 new_sidedata_offset = new_sidedata_file.tell()
382 new_sidedata_offset = new_sidedata_file.tell()
379 if 0 < sidedata_size:
383 if 0 < sidedata_size:
380 old_sidedata_offset = entry[ENTRY_SIDEDATA_OFFSET]
384 old_sidedata_offset = entry[ENTRY_SIDEDATA_OFFSET]
381 old_sidedata_file.seek(old_sidedata_offset)
385 old_sidedata_file.seek(old_sidedata_offset)
382 new_sidedata = old_sidedata_file.read(sidedata_size)
386 new_sidedata = old_sidedata_file.read(sidedata_size)
383 new_sidedata_file.write(new_sidedata)
387 new_sidedata_file.write(new_sidedata)
384
388
385 data_uncompressed_length = entry[ENTRY_DATA_UNCOMPRESSED_LENGTH]
389 data_uncompressed_length = entry[ENTRY_DATA_UNCOMPRESSED_LENGTH]
386 sd_com_mode = entry[ENTRY_SIDEDATA_COMPRESSION_MODE]
390 sd_com_mode = entry[ENTRY_SIDEDATA_COMPRESSION_MODE]
387 assert data_delta_base <= rev, (data_delta_base, rev)
391 assert data_delta_base <= rev, (data_delta_base, rev)
388
392
389 new_entry = revlogutils.entry(
393 new_entry = revlogutils.entry(
390 flags=flags,
394 flags=flags,
391 data_offset=new_data_offset,
395 data_offset=new_data_offset,
392 data_compressed_length=new_data_size,
396 data_compressed_length=new_data_size,
393 data_uncompressed_length=data_uncompressed_length,
397 data_uncompressed_length=data_uncompressed_length,
394 data_delta_base=data_delta_base,
398 data_delta_base=data_delta_base,
395 link_rev=entry[ENTRY_LINK_REV],
399 link_rev=entry[ENTRY_LINK_REV],
396 parent_rev_1=entry[ENTRY_PARENT_1],
400 parent_rev_1=entry[ENTRY_PARENT_1],
397 parent_rev_2=entry[ENTRY_PARENT_2],
401 parent_rev_2=entry[ENTRY_PARENT_2],
398 node_id=entry[ENTRY_NODE_ID],
402 node_id=entry[ENTRY_NODE_ID],
399 sidedata_offset=new_sidedata_offset,
403 sidedata_offset=new_sidedata_offset,
400 sidedata_compressed_length=sidedata_size,
404 sidedata_compressed_length=sidedata_size,
401 data_compression_mode=d_comp_mode,
405 data_compression_mode=d_comp_mode,
402 sidedata_compression_mode=sd_com_mode,
406 sidedata_compression_mode=sd_com_mode,
403 )
407 )
404 revlog.index.append(new_entry)
408 revlog.index.append(new_entry)
405 entry_bin = revlog.index.entry_binary(rev)
409 entry_bin = revlog.index.entry_binary(rev)
406 new_index_file.write(entry_bin)
410 new_index_file.write(entry_bin)
407
411
408 revlog._docket.index_end = new_index_file.tell()
412 revlog._docket.index_end = new_index_file.tell()
409 revlog._docket.data_end = new_data_file.tell()
413 revlog._docket.data_end = new_data_file.tell()
410 revlog._docket.sidedata_end = new_sidedata_file.tell()
414 revlog._docket.sidedata_end = new_sidedata_file.tell()
411
415
412
416
413 def _rewrite_censor(
417 def _rewrite_censor(
414 revlog,
418 revlog,
415 old_index,
419 old_index,
416 all_files,
420 all_files,
417 rev,
421 rev,
418 tombstone,
422 tombstone,
419 ):
423 ):
420 """rewrite and append a censored revision"""
424 """rewrite and append a censored revision"""
421 (
425 (
422 old_data_file,
426 old_data_file,
423 old_sidedata_file,
427 old_sidedata_file,
424 new_index_file,
428 new_index_file,
425 new_data_file,
429 new_data_file,
426 new_sidedata_file,
430 new_sidedata_file,
427 ) = all_files
431 ) = all_files
428 entry = old_index[rev]
432 entry = old_index[rev]
429
433
430 # XXX consider trying the default compression too
434 # XXX consider trying the default compression too
431 new_data_size = len(tombstone)
435 new_data_size = len(tombstone)
432 new_data_offset = new_data_file.tell()
436 new_data_offset = new_data_file.tell()
433 new_data_file.write(tombstone)
437 new_data_file.write(tombstone)
434
438
435 # we are not adding any sidedata as they might leak info about the censored version
439 # we are not adding any sidedata as they might leak info about the censored version
436
440
437 link_rev = entry[ENTRY_LINK_REV]
441 link_rev = entry[ENTRY_LINK_REV]
438
442
439 p1 = entry[ENTRY_PARENT_1]
443 p1 = entry[ENTRY_PARENT_1]
440 p2 = entry[ENTRY_PARENT_2]
444 p2 = entry[ENTRY_PARENT_2]
441
445
442 new_entry = revlogutils.entry(
446 new_entry = revlogutils.entry(
443 flags=constants.REVIDX_ISCENSORED,
447 flags=constants.REVIDX_ISCENSORED,
444 data_offset=new_data_offset,
448 data_offset=new_data_offset,
445 data_compressed_length=new_data_size,
449 data_compressed_length=new_data_size,
446 data_uncompressed_length=new_data_size,
450 data_uncompressed_length=new_data_size,
447 data_delta_base=rev,
451 data_delta_base=rev,
448 link_rev=link_rev,
452 link_rev=link_rev,
449 parent_rev_1=p1,
453 parent_rev_1=p1,
450 parent_rev_2=p2,
454 parent_rev_2=p2,
451 node_id=entry[ENTRY_NODE_ID],
455 node_id=entry[ENTRY_NODE_ID],
452 sidedata_offset=0,
456 sidedata_offset=0,
453 sidedata_compressed_length=0,
457 sidedata_compressed_length=0,
454 data_compression_mode=COMP_MODE_PLAIN,
458 data_compression_mode=COMP_MODE_PLAIN,
455 sidedata_compression_mode=COMP_MODE_PLAIN,
459 sidedata_compression_mode=COMP_MODE_PLAIN,
456 )
460 )
457 revlog.index.append(new_entry)
461 revlog.index.append(new_entry)
458 entry_bin = revlog.index.entry_binary(rev)
462 entry_bin = revlog.index.entry_binary(rev)
459 new_index_file.write(entry_bin)
463 new_index_file.write(entry_bin)
460 revlog._docket.index_end = new_index_file.tell()
464 revlog._docket.index_end = new_index_file.tell()
461 revlog._docket.data_end = new_data_file.tell()
465 revlog._docket.data_end = new_data_file.tell()
General Comments 0
You need to be logged in to leave comments. Login now