239 ) |
239 ) |
240 |
240 |
241 hexdigits = b'0123456789abcdefABCDEF' |
241 hexdigits = b'0123456789abcdefABCDEF' |
242 |
242 |
243 |
243 |
|
244 @attr.s() |
|
245 class FeatureConfig: |
|
246 """Hold configuration values about the available revlog features""" |
|
247 |
|
248 # the default compression engine |
|
249 compression_engine = attr.ib(default=b'zlib') |
|
250 # compression engines options |
|
251 compression_engine_options = attr.ib(default=attr.Factory(dict)) |
|
252 |
|
253 # can we use censor on this revlog |
|
254 censorable = attr.ib(default=False) |
|
255 # does this revlog use the "side data" feature |
|
256 has_side_data = attr.ib(default=False) |
|
257 # might remove rank configuration once the computation has no impact |
|
258 compute_rank = attr.ib(default=False) |
|
259 # parent order is supposed to be semantically irrelevant, so we |
|
260 # normally resort parents to ensure that the first parent is non-null, |
|
261 # if there is a non-null parent at all. |
|
262 # filelog abuses the parent order as flag to mark some instances of |
|
263 # meta-encoded files, so allow it to disable this behavior. |
|
264 canonical_parent_order = attr.ib(default=False) |
|
265 # can ellipsis commit be used |
|
266 enable_ellipsis = attr.ib(default=False) |
|
267 |
|
268 |
|
269 @attr.s() |
|
270 class DataConfig: |
|
271 """Hold configuration value about how the revlog data are read""" |
|
272 |
|
273 # should we try to open the "pending" version of the revlog |
|
274 try_pending = attr.ib(default=False) |
|
275 # should we try to open the "splitted" version of the revlog |
|
276 try_split = attr.ib(default=False) |
|
277 # When True, indexfile should be opened with checkambig=True at writing, |
|
278 # to avoid file stat ambiguity. |
|
279 check_ambig = attr.ib(default=False) |
|
280 |
|
281 # If true, use mmap instead of reading to deal with large index |
|
282 mmap_large_index = attr.ib(default=False) |
|
283 # how much data is large |
|
284 mmap_index_threshold = attr.ib(default=None) |
|
285 # How much data to read and cache into the raw revlog data cache. |
|
286 chunk_cache_size = attr.ib(default=65536) |
|
287 |
|
288 # Allow sparse reading of the revlog data |
|
289 with_sparse_read = attr.ib(default=False) |
|
290 # minimal density of a sparse read chunk |
|
291 sr_density_threshold = attr.ib(default=0.50) |
|
292 # minimal size of data we skip when performing sparse read |
|
293 sr_min_gap_size = attr.ib(default=262144) |
|
294 |
|
295 # are delta encoded against arbitrary bases. |
|
296 generaldelta = attr.ib(default=False) |
|
297 |
|
298 |
|
299 @attr.s() |
|
300 class DeltaConfig: |
|
301 """Hold configuration value about how new delta are computed |
|
302 |
|
303 Some attributes are duplicated from DataConfig to help havign each object |
|
304 self contained. |
|
305 """ |
|
306 |
|
307 # can delta be encoded against arbitrary bases. |
|
308 general_delta = attr.ib(default=False) |
|
309 # Allow sparse writing of the revlog data |
|
310 sparse_revlog = attr.ib(default=False) |
|
311 # maximum length of a delta chain |
|
312 max_chain_len = attr.ib(default=None) |
|
313 # Maximum distance between delta chain base start and end |
|
314 max_deltachain_span = attr.ib(default=-1) |
|
315 # If `upper_bound_comp` is not None, this is the expected maximal gain from |
|
316 # compression for the data content. |
|
317 upper_bound_comp = attr.ib(default=None) |
|
318 # Should we try a delta against both parent |
|
319 delta_both_parents = attr.ib(default=True) |
|
320 # Test delta base candidate group by chunk of this maximal size. |
|
321 candidate_group_chunk_size = attr.ib(default=0) |
|
322 # Should we display debug information about delta computation |
|
323 debug_delta = attr.ib(default=False) |
|
324 # trust incoming delta by default |
|
325 lazy_delta = attr.ib(default=True) |
|
326 # trust the base of incoming delta by default |
|
327 lazy_delta_base = attr.ib(default=False) |
|
328 |
|
329 |
244 class revlog: |
330 class revlog: |
245 """ |
331 """ |
246 the underlying revision storage object |
332 the underlying revision storage object |
247 |
333 |
248 A revlog consists of two parts, an index and the revision data. |
334 A revlog consists of two parts, an index and the revision data. |
346 self._nodemap_file = nodemaputil.get_nodemap_file(self) |
432 self._nodemap_file = nodemaputil.get_nodemap_file(self) |
347 |
433 |
348 assert target[0] in ALL_KINDS |
434 assert target[0] in ALL_KINDS |
349 assert len(target) == 2 |
435 assert len(target) == 2 |
350 self.target = target |
436 self.target = target |
351 # When True, indexfile is opened with checkambig=True at writing, to |
437 self.feature_config = FeatureConfig( |
352 # avoid file stat ambiguity. |
438 censorable=censorable, |
353 self._checkambig = checkambig |
439 canonical_parent_order=canonical_parent_order, |
354 self._mmaplargeindex = mmaplargeindex |
440 ) |
355 self._censorable = censorable |
441 self.data_config = DataConfig( |
|
442 check_ambig=checkambig, |
|
443 mmap_large_index=mmaplargeindex, |
|
444 ) |
|
445 self.delta_config = DeltaConfig() |
|
446 |
356 # 3-tuple of (node, rev, text) for a raw revision. |
447 # 3-tuple of (node, rev, text) for a raw revision. |
357 self._revisioncache = None |
448 self._revisioncache = None |
358 # Maps rev to chain base rev. |
449 # Maps rev to chain base rev. |
359 self._chainbasecache = util.lrucachedict(100) |
450 self._chainbasecache = util.lrucachedict(100) |
360 # 2-tuple of (offset, data) of raw data from the revlog at an offset. |
451 # 2-tuple of (offset, data) of raw data from the revlog at an offset. |
361 self._chunkcache = (0, b'') |
452 self._chunkcache = (0, b'') |
362 # How much data to read and cache into the raw revlog data cache. |
453 |
363 self._chunkcachesize = 65536 |
|
364 self._maxchainlen = None |
|
365 self._deltabothparents = True |
|
366 self._candidate_group_chunk_size = 0 |
|
367 self._debug_delta = False |
|
368 self.index = None |
454 self.index = None |
369 self._docket = None |
455 self._docket = None |
370 self._nodemap_docket = None |
456 self._nodemap_docket = None |
371 # Mapping of partial identifiers to full nodes. |
457 # Mapping of partial identifiers to full nodes. |
372 self._pcache = {} |
458 self._pcache = {} |
373 # Mapping of revision integer to full node. |
|
374 self._compengine = b'zlib' |
|
375 self._compengineopts = {} |
|
376 self._maxdeltachainspan = -1 |
|
377 self._withsparseread = False |
|
378 self._sparserevlog = False |
|
379 self.hassidedata = False |
|
380 self._srdensitythreshold = 0.50 |
|
381 self._srmingapsize = 262144 |
|
382 |
459 |
383 # other optionnals features |
460 # other optionnals features |
384 |
|
385 # might remove rank configuration once the computation has no impact |
|
386 self._compute_rank = False |
|
387 |
461 |
388 # Make copy of flag processors so each revlog instance can support |
462 # Make copy of flag processors so each revlog instance can support |
389 # custom flags. |
463 # custom flags. |
390 self._flagprocessors = dict(flagutil.flagprocessors) |
464 self._flagprocessors = dict(flagutil.flagprocessors) |
391 |
465 |
396 |
470 |
397 self._loadindex() |
471 self._loadindex() |
398 |
472 |
399 self._concurrencychecker = concurrencychecker |
473 self._concurrencychecker = concurrencychecker |
400 |
474 |
401 # parent order is supposed to be semantically irrelevant, so we |
475 @property |
402 # normally resort parents to ensure that the first parent is non-null, |
476 def _generaldelta(self): |
403 # if there is a non-null parent at all. |
477 """temporary compatibility proxy""" |
404 # filelog abuses the parent order as flag to mark some instances of |
478 return self.delta_config.general_delta |
405 # meta-encoded files, so allow it to disable this behavior. |
479 |
406 self.canonical_parent_order = canonical_parent_order |
480 @property |
|
481 def _checkambig(self): |
|
482 """temporary compatibility proxy""" |
|
483 return self.data_config.check_ambig |
|
484 |
|
485 @property |
|
486 def _mmaplargeindex(self): |
|
487 """temporary compatibility proxy""" |
|
488 return self.data_config.mmap_large_index |
|
489 |
|
490 @property |
|
491 def _censorable(self): |
|
492 """temporary compatibility proxy""" |
|
493 return self.feature_config.censorable |
|
494 |
|
495 @property |
|
496 def _chunkcachesize(self): |
|
497 """temporary compatibility proxy""" |
|
498 return self.data_config.chunk_cache_size |
|
499 |
|
500 @property |
|
501 def _maxchainlen(self): |
|
502 """temporary compatibility proxy""" |
|
503 return self.delta_config.max_chain_len |
|
504 |
|
505 @property |
|
506 def _deltabothparents(self): |
|
507 """temporary compatibility proxy""" |
|
508 return self.delta_config.delta_both_parents |
|
509 |
|
510 @property |
|
511 def _candidate_group_chunk_size(self): |
|
512 """temporary compatibility proxy""" |
|
513 return self.delta_config.candidate_group_chunk_size |
|
514 |
|
515 @property |
|
516 def _debug_delta(self): |
|
517 """temporary compatibility proxy""" |
|
518 return self.delta_config.debug_delta |
|
519 |
|
520 @property |
|
521 def _compengine(self): |
|
522 """temporary compatibility proxy""" |
|
523 return self.feature_config.compression_engine |
|
524 |
|
525 @property |
|
526 def _compengineopts(self): |
|
527 """temporary compatibility proxy""" |
|
528 return self.feature_config.compression_engine_options |
|
529 |
|
530 @property |
|
531 def _maxdeltachainspan(self): |
|
532 """temporary compatibility proxy""" |
|
533 return self.delta_config.max_deltachain_span |
|
534 |
|
535 @property |
|
536 def _withsparseread(self): |
|
537 """temporary compatibility proxy""" |
|
538 return self.data_config.with_sparse_read |
|
539 |
|
540 @property |
|
541 def _sparserevlog(self): |
|
542 """temporary compatibility proxy""" |
|
543 return self.delta_config.sparse_revlog |
|
544 |
|
545 @property |
|
546 def hassidedata(self): |
|
547 """temporary compatibility proxy""" |
|
548 return self.feature_config.has_side_data |
|
549 |
|
550 @property |
|
551 def _srdensitythreshold(self): |
|
552 """temporary compatibility proxy""" |
|
553 return self.data_config.sr_density_threshold |
|
554 |
|
555 @property |
|
556 def _srmingapsize(self): |
|
557 """temporary compatibility proxy""" |
|
558 return self.data_config.sr_min_gap_size |
|
559 |
|
560 @property |
|
561 def _compute_rank(self): |
|
562 """temporary compatibility proxy""" |
|
563 return self.feature_config.compute_rank |
|
564 |
|
565 @property |
|
566 def canonical_parent_order(self): |
|
567 """temporary compatibility proxy""" |
|
568 return self.feature_config.canonical_parent_order |
|
569 |
|
570 @property |
|
571 def _lazydelta(self): |
|
572 """temporary compatibility proxy""" |
|
573 return self.delta_config.lazy_delta |
|
574 |
|
575 @property |
|
576 def _lazydeltabase(self): |
|
577 """temporary compatibility proxy""" |
|
578 return self.delta_config.lazy_delta_base |
407 |
579 |
408 def _init_opts(self): |
580 def _init_opts(self): |
409 """process options (from above/config) to setup associated default revlog mode |
581 """process options (from above/config) to setup associated default revlog mode |
410 |
582 |
411 These values might be affected when actually reading on disk information. |
583 These values might be affected when actually reading on disk information. |
437 new_header = REVLOGV0 |
610 new_header = REVLOGV0 |
438 else: |
611 else: |
439 new_header = REVLOG_DEFAULT_VERSION |
612 new_header = REVLOG_DEFAULT_VERSION |
440 |
613 |
441 if b'chunkcachesize' in opts: |
614 if b'chunkcachesize' in opts: |
442 self._chunkcachesize = opts[b'chunkcachesize'] |
615 self.data_config.chunk_cache_size = opts[b'chunkcachesize'] |
443 if b'maxchainlen' in opts: |
616 if b'maxchainlen' in opts: |
444 self._maxchainlen = opts[b'maxchainlen'] |
617 self.delta_config.max_chain_len = opts[b'maxchainlen'] |
445 if b'deltabothparents' in opts: |
618 if b'deltabothparents' in opts: |
446 self._deltabothparents = opts[b'deltabothparents'] |
619 self.delta_config.delta_both_parents = opts[b'deltabothparents'] |
447 dps_cgds = opts.get(b'delta-parent-search.candidate-group-chunk-size') |
620 dps_cgds = opts.get(b'delta-parent-search.candidate-group-chunk-size') |
448 if dps_cgds: |
621 if dps_cgds: |
449 self._candidate_group_chunk_size = dps_cgds |
622 self.delta_config.candidate_group_chunk_size = dps_cgds |
450 self._lazydelta = bool(opts.get(b'lazydelta', True)) |
623 if b'lazydelta' in opts: |
451 self._lazydeltabase = False |
624 self.delta_config.lazy_delta = bool(opts[b'lazydelta']) |
452 if self._lazydelta: |
625 if self._lazydelta and b'lazydeltabase' in opts: |
453 self._lazydeltabase = bool(opts.get(b'lazydeltabase', False)) |
626 self.delta_config.lazy_delta_base = opts[b'lazydeltabase'] |
454 if b'debug-delta' in opts: |
627 if b'debug-delta' in opts: |
455 self._debug_delta = opts[b'debug-delta'] |
628 self.delta_config.debug_delta = opts[b'debug-delta'] |
456 if b'compengine' in opts: |
629 if b'compengine' in opts: |
457 self._compengine = opts[b'compengine'] |
630 self.feature_config.compression_engine = opts[b'compengine'] |
|
631 comp_engine_opts = self.feature_config.compression_engine_options |
458 if b'zlib.level' in opts: |
632 if b'zlib.level' in opts: |
459 self._compengineopts[b'zlib.level'] = opts[b'zlib.level'] |
633 comp_engine_opts[b'zlib.level'] = opts[b'zlib.level'] |
460 if b'zstd.level' in opts: |
634 if b'zstd.level' in opts: |
461 self._compengineopts[b'zstd.level'] = opts[b'zstd.level'] |
635 comp_engine_opts[b'zstd.level'] = opts[b'zstd.level'] |
462 if b'maxdeltachainspan' in opts: |
636 if b'maxdeltachainspan' in opts: |
463 self._maxdeltachainspan = opts[b'maxdeltachainspan'] |
637 self.delta_config.max_deltachain_span = opts[b'maxdeltachainspan'] |
464 if self._mmaplargeindex and b'mmapindexthreshold' in opts: |
638 if self._mmaplargeindex and b'mmapindexthreshold' in opts: |
465 mmapindexthreshold = opts[b'mmapindexthreshold'] |
639 mmapindexthreshold = opts[b'mmapindexthreshold'] |
466 self._sparserevlog = bool(opts.get(b'sparse-revlog', False)) |
640 self.data_config.mmap_index_threshold = mmapindexthreshold |
467 withsparseread = bool(opts.get(b'with-sparse-read', False)) |
641 if b'sparse-revlog' in opts: |
468 # sparse-revlog forces sparse-read |
642 self.delta_config.sparse_revlog = bool(opts[b'sparse-revlog']) |
469 self._withsparseread = self._sparserevlog or withsparseread |
643 if self.delta_config.sparse_revlog: |
|
644 # sparse-revlog forces sparse-read |
|
645 self.data_config.with_sparse_read = True |
|
646 elif b'with-sparse-read' in opts: |
|
647 self.data_config.with_sparse_read = bool(opts[b'with-sparse-read']) |
470 if b'sparse-read-density-threshold' in opts: |
648 if b'sparse-read-density-threshold' in opts: |
471 self._srdensitythreshold = opts[b'sparse-read-density-threshold'] |
649 self.data_config.sr_density_threshold = opts[ |
|
650 b'sparse-read-density-threshold' |
|
651 ] |
472 if b'sparse-read-min-gap-size' in opts: |
652 if b'sparse-read-min-gap-size' in opts: |
473 self._srmingapsize = opts[b'sparse-read-min-gap-size'] |
653 self.data_config.sr_min_gap_size = opts[b'sparse-read-min-gap-size'] |
474 if opts.get(b'enableellipsis'): |
654 if opts.get(b'enableellipsis'): |
|
655 self.feature_config.enable_ellipsis = True |
475 self._flagprocessors[REVIDX_ELLIPSIS] = ellipsisprocessor |
656 self._flagprocessors[REVIDX_ELLIPSIS] = ellipsisprocessor |
476 |
657 |
477 # revlog v0 doesn't have flag processors |
658 # revlog v0 doesn't have flag processors |
478 for flag, processor in opts.get(b'flagprocessors', {}).items(): |
659 for flag, processor in opts.get(b'flagprocessors', {}).items(): |
479 flagutil.insertflagprocessor(flag, processor, self._flagprocessors) |
660 flagutil.insertflagprocessor(flag, processor, self._flagprocessors) |
480 |
661 |
481 if self._chunkcachesize <= 0: |
662 chunk_cache_size = self.data_config.chunk_cache_size |
|
663 if chunk_cache_size <= 0: |
482 raise error.RevlogError( |
664 raise error.RevlogError( |
483 _(b'revlog chunk cache size %r is not greater than 0') |
665 _(b'revlog chunk cache size %r is not greater than 0') |
484 % self._chunkcachesize |
666 % chunk_cache_size |
485 ) |
667 ) |
486 elif self._chunkcachesize & (self._chunkcachesize - 1): |
668 elif chunk_cache_size & (chunk_cache_size - 1): |
487 raise error.RevlogError( |
669 raise error.RevlogError( |
488 _(b'revlog chunk cache size %r is not a power of 2') |
670 _(b'revlog chunk cache size %r is not a power of 2') |
489 % self._chunkcachesize |
671 % chunk_cache_size |
490 ) |
672 ) |
491 force_nodemap = opts.get(b'devel-force-nodemap', False) |
673 force_nodemap = opts.get(b'devel-force-nodemap', False) |
492 return new_header, mmapindexthreshold, force_nodemap |
674 return new_header, mmapindexthreshold, force_nodemap |
493 |
675 |
494 def _get_data(self, filepath, mmap_threshold, size=None): |
676 def _get_data(self, filepath, mmap_threshold, size=None): |
3195 oldlazydeltabase = destrevlog._lazydeltabase |
3379 oldlazydeltabase = destrevlog._lazydeltabase |
3196 oldamd = destrevlog._deltabothparents |
3380 oldamd = destrevlog._deltabothparents |
3197 |
3381 |
3198 try: |
3382 try: |
3199 if deltareuse == self.DELTAREUSEALWAYS: |
3383 if deltareuse == self.DELTAREUSEALWAYS: |
3200 destrevlog._lazydeltabase = True |
3384 destrevlog.delta_config.lazy_delta_base = True |
3201 destrevlog._lazydelta = True |
3385 destrevlog.delta_config.lazy_delta = True |
3202 elif deltareuse == self.DELTAREUSESAMEREVS: |
3386 elif deltareuse == self.DELTAREUSESAMEREVS: |
3203 destrevlog._lazydeltabase = False |
3387 destrevlog.delta_config.lazy_delta_base = False |
3204 destrevlog._lazydelta = True |
3388 destrevlog.delta_config.lazy_delta = True |
3205 elif deltareuse == self.DELTAREUSENEVER: |
3389 elif deltareuse == self.DELTAREUSENEVER: |
3206 destrevlog._lazydeltabase = False |
3390 destrevlog.delta_config.lazy_delta_base = False |
3207 destrevlog._lazydelta = False |
3391 destrevlog.delta_config.lazy_delta = False |
3208 |
3392 |
3209 destrevlog._deltabothparents = forcedeltabothparents or oldamd |
3393 delta_both_parents = forcedeltabothparents or oldamd |
|
3394 destrevlog.delta_config.delta_both_parents = delta_both_parents |
3210 |
3395 |
3211 with self.reading(): |
3396 with self.reading(): |
3212 self._clone( |
3397 self._clone( |
3213 tr, |
3398 tr, |
3214 destrevlog, |
3399 destrevlog, |