comparison mercurial/revlog.py @ 51083:c3748f38dcd0

revlog: create a iteration of a _InnerRevlog object within the revlog The goal of this object is to isolate a sub-API that can be implemented by a compiled object (e.g. Rust). So the boundary of this object will be arbitrary depending of what can we easily implemented in the Compiled code. For now, we start simple, and move the code that manage the IO objects in the inner object. More will come in the coming changesets. Note: the object definition could live in the different module to thin the `revlog.py` file, however there are other better candidate for extraction first and I have enought patch stacked on top of the this one for the split in this patch not to be worth it. So I leave this to future me.
author Pierre-Yves David <pierre-yves.david@octobus.net>
date Tue, 17 Oct 2023 06:02:33 +0200
parents 118c99c6092b
children df50a1592e0c
comparison
equal deleted inserted replaced
51082:118c99c6092b 51083:c3748f38dcd0
335 lazy_delta = attr.ib(default=True) 335 lazy_delta = attr.ib(default=True)
336 # trust the base of incoming delta by default 336 # trust the base of incoming delta by default
337 lazy_delta_base = attr.ib(default=False) 337 lazy_delta_base = attr.ib(default=False)
338 338
339 339
340 class _InnerRevlog:
341 """An inner layer of the revlog object
342
343 That layer exist to be able to delegate some operation to Rust, its
344 boundaries are arbitrary and based on what we can delegate to Rust.
345 """
346
347 def __init__(
348 self,
349 opener,
350 index,
351 index_file,
352 data_file,
353 sidedata_file,
354 inline,
355 data_config,
356 chunk_cache,
357 ):
358 self.opener = opener
359 self.index = index
360
361 self.index_file = index_file
362 self.data_file = data_file
363 self.sidedata_file = sidedata_file
364 self.inline = inline
365 self.data_config = data_config
366
367 # index
368
369 # 3-tuple of file handles being used for active writing.
370 self._writinghandles = None
371
372 self._segmentfile = randomaccessfile.randomaccessfile(
373 self.opener,
374 (self.index_file if self.inline else self.data_file),
375 self.data_config.chunk_cache_size,
376 chunk_cache,
377 )
378 self._segmentfile_sidedata = randomaccessfile.randomaccessfile(
379 self.opener,
380 self.sidedata_file,
381 self.data_config.chunk_cache_size,
382 )
383
384 # Derived from index values.
385
386 def start(self, rev):
387 """the offset of the data chunk for this revision"""
388 return int(self.index[rev][0] >> 16)
389
390 def length(self, rev):
391 """the length of the data chunk for this revision"""
392 return self.index[rev][1]
393
394 def end(self, rev):
395 """the end of the data chunk for this revision"""
396 return self.start(rev) + self.length(rev)
397
398 @contextlib.contextmanager
399 def reading(self):
400 """Context manager that keeps data and sidedata files open for reading"""
401 if len(self.index) == 0:
402 yield # nothing to be read
403 else:
404 with self._segmentfile.reading():
405 with self._segmentfile_sidedata.reading():
406 yield
407
408 @property
409 def is_writing(self):
410 """True is a writing context is open"""
411 return self._writinghandles is not None
412
413 @contextlib.contextmanager
414 def writing(self, transaction, data_end=None, sidedata_end=None):
415 """Open the revlog files for writing
416
417 Add content to a revlog should be done within such context.
418 """
419 if self.is_writing:
420 yield
421 else:
422 ifh = dfh = sdfh = None
423 try:
424 r = len(self.index)
425 # opening the data file.
426 dsize = 0
427 if r:
428 dsize = self.end(r - 1)
429 dfh = None
430 if not self.inline:
431 try:
432 dfh = self.opener(self.data_file, mode=b"r+")
433 if data_end is None:
434 dfh.seek(0, os.SEEK_END)
435 else:
436 dfh.seek(data_end, os.SEEK_SET)
437 except FileNotFoundError:
438 dfh = self.opener(self.data_file, mode=b"w+")
439 transaction.add(self.data_file, dsize)
440 if self.sidedata_file is not None:
441 assert sidedata_end is not None
442 # revlog-v2 does not inline, help Pytype
443 assert dfh is not None
444 try:
445 sdfh = self.opener(self.sidedata_file, mode=b"r+")
446 dfh.seek(sidedata_end, os.SEEK_SET)
447 except FileNotFoundError:
448 sdfh = self.opener(self.sidedata_file, mode=b"w+")
449 transaction.add(self.sidedata_file, sidedata_end)
450
451 # opening the index file.
452 isize = r * self.index.entry_size
453 ifh = self.__index_write_fp()
454 if self.inline:
455 transaction.add(self.index_file, dsize + isize)
456 else:
457 transaction.add(self.index_file, isize)
458 # exposing all file handle for writing.
459 self._writinghandles = (ifh, dfh, sdfh)
460 self._segmentfile.writing_handle = ifh if self.inline else dfh
461 self._segmentfile_sidedata.writing_handle = sdfh
462 yield
463 finally:
464 self._writinghandles = None
465 self._segmentfile.writing_handle = None
466 self._segmentfile_sidedata.writing_handle = None
467 if dfh is not None:
468 dfh.close()
469 if sdfh is not None:
470 sdfh.close()
471 # closing the index file last to avoid exposing referent to
472 # potential unflushed data content.
473 if ifh is not None:
474 ifh.close()
475
476 def __index_write_fp(self, index_end=None):
477 """internal method to open the index file for writing
478
479 You should not use this directly and use `_writing` instead
480 """
481 try:
482 f = self.opener(
483 self.index_file,
484 mode=b"r+",
485 checkambig=self.data_config.check_ambig,
486 )
487 if index_end is None:
488 f.seek(0, os.SEEK_END)
489 else:
490 f.seek(index_end, os.SEEK_SET)
491 return f
492 except FileNotFoundError:
493 return self.opener(
494 self.index_file,
495 mode=b"w+",
496 checkambig=self.data_config.check_ambig,
497 )
498
499 def __index_new_fp(self):
500 """internal method to create a new index file for writing
501
502 You should not use this unless you are upgrading from inline revlog
503 """
504 return self.opener(
505 self.index_file,
506 mode=b"w",
507 checkambig=self.data_config.check_ambig,
508 atomictemp=True,
509 )
510
511
340 class revlog: 512 class revlog:
341 """ 513 """
342 the underlying revision storage object 514 the underlying revision storage object
343 515
344 A revlog consists of two parts, an index and the revision data. 516 A revlog consists of two parts, an index and the revision data.
475 # other optionnals features 647 # other optionnals features
476 648
477 # Make copy of flag processors so each revlog instance can support 649 # Make copy of flag processors so each revlog instance can support
478 # custom flags. 650 # custom flags.
479 self._flagprocessors = dict(flagutil.flagprocessors) 651 self._flagprocessors = dict(flagutil.flagprocessors)
480
481 # 3-tuple of file handles being used for active writing.
482 self._writinghandles = None
483 # prevent nesting of addgroup 652 # prevent nesting of addgroup
484 self._adding_group = None 653 self._adding_group = None
485 654
486 self._loadindex() 655 chunk_cache = self._loadindex()
656 self._load_inner(chunk_cache)
487 657
488 self._concurrencychecker = concurrencychecker 658 self._concurrencychecker = concurrencychecker
489 659
490 @property 660 @property
491 def _generaldelta(self): 661 def _generaldelta(self):
1005 except (ValueError, IndexError): 1175 except (ValueError, IndexError):
1006 raise error.RevlogError( 1176 raise error.RevlogError(
1007 _(b"index %s is corrupted") % self.display_id 1177 _(b"index %s is corrupted") % self.display_id
1008 ) 1178 )
1009 self.index = index 1179 self.index = index
1010 self._segmentfile = randomaccessfile.randomaccessfile(
1011 self.opener,
1012 (self._indexfile if self._inline else self._datafile),
1013 self.data_config.chunk_cache_size,
1014 chunkcache,
1015 )
1016 self._segmentfile_sidedata = randomaccessfile.randomaccessfile(
1017 self.opener,
1018 self._sidedatafile,
1019 self.data_config.chunk_cache_size,
1020 )
1021 # revnum -> (chain-length, sum-delta-length) 1180 # revnum -> (chain-length, sum-delta-length)
1022 self._chaininfocache = util.lrucachedict(500) 1181 self._chaininfocache = util.lrucachedict(500)
1023 # revlog header -> revlog compressor 1182 # revlog header -> revlog compressor
1024 self._decompressors = {} 1183 self._decompressors = {}
1184
1185 return chunkcache
1186
1187 def _load_inner(self, chunk_cache):
1188 self._inner = _InnerRevlog(
1189 opener=self.opener,
1190 index=self.index,
1191 index_file=self._indexfile,
1192 data_file=self._datafile,
1193 sidedata_file=self._sidedatafile,
1194 inline=self._inline,
1195 data_config=self.data_config,
1196 chunk_cache=chunk_cache,
1197 )
1025 1198
1026 def get_revlog(self): 1199 def get_revlog(self):
1027 """simple function to mirror API of other not-really-revlog API""" 1200 """simple function to mirror API of other not-really-revlog API"""
1028 return self 1201 return self
1029 1202
1071 return None 1244 return None
1072 t = self._docket.default_compression_header 1245 t = self._docket.default_compression_header
1073 c = self._get_decompressor(t) 1246 c = self._get_decompressor(t)
1074 return c.decompress 1247 return c.decompress
1075 1248
1076 def __index_write_fp(self):
1077 # You should not use this directly and use `_writing` instead
1078 try:
1079 f = self.opener(
1080 self._indexfile,
1081 mode=b"r+",
1082 checkambig=self.data_config.check_ambig,
1083 )
1084 if self._docket is None:
1085 f.seek(0, os.SEEK_END)
1086 else:
1087 f.seek(self._docket.index_end, os.SEEK_SET)
1088 return f
1089 except FileNotFoundError:
1090 return self.opener(
1091 self._indexfile,
1092 mode=b"w+",
1093 checkambig=self.data_config.check_ambig,
1094 )
1095
1096 def __index_new_fp(self):
1097 # You should not use this unless you are upgrading from inline revlog
1098 return self.opener(
1099 self._indexfile,
1100 mode=b"w",
1101 checkambig=self.data_config.check_ambig,
1102 atomictemp=True,
1103 )
1104
1105 def _datafp(self, mode=b'r'): 1249 def _datafp(self, mode=b'r'):
1106 """file object for the revlog's data file""" 1250 """file object for the revlog's data file"""
1107 return self.opener(self._datafile, mode=mode) 1251 return self.opener(self._datafile, mode=mode)
1108 1252
1109 def tiprev(self): 1253 def tiprev(self):
1158 1302
1159 def clearcaches(self): 1303 def clearcaches(self):
1160 """Clear in-memory caches""" 1304 """Clear in-memory caches"""
1161 self._revisioncache = None 1305 self._revisioncache = None
1162 self._chainbasecache.clear() 1306 self._chainbasecache.clear()
1163 self._segmentfile.clear_cache() 1307 self._inner._segmentfile.clear_cache()
1164 self._segmentfile_sidedata.clear_cache() 1308 self._inner._segmentfile_sidedata.clear_cache()
1165 self._pcache = {} 1309 self._pcache = {}
1166 self._nodemap_docket = None 1310 self._nodemap_docket = None
1167 self.index.clearcaches() 1311 self.index.clearcaches()
1168 # The python code is the one responsible for validating the docket, we 1312 # The python code is the one responsible for validating the docket, we
1169 # end up having to refresh it here. 1313 # end up having to refresh it here.
2039 if self._inline: 2183 if self._inline:
2040 start += (startrev + 1) * self.index.entry_size 2184 start += (startrev + 1) * self.index.entry_size
2041 end += (endrev + 1) * self.index.entry_size 2185 end += (endrev + 1) * self.index.entry_size
2042 length = end - start 2186 length = end - start
2043 2187
2044 return start, self._segmentfile.read_chunk(start, length) 2188 return start, self._inner._segmentfile.read_chunk(start, length)
2045 2189
2046 def _chunk(self, rev): 2190 def _chunk(self, rev):
2047 """Obtain a single decompressed chunk for a revision. 2191 """Obtain a single decompressed chunk for a revision.
2048 2192
2049 Accepts an integer revision and an optional already-open file handle 2193 Accepts an integer revision and an optional already-open file handle
2316 offset = sidedata_offset 2460 offset = sidedata_offset
2317 length = sidedata_size 2461 length = sidedata_size
2318 m = FILE_TOO_SHORT_MSG % (filename, length, offset, end) 2462 m = FILE_TOO_SHORT_MSG % (filename, length, offset, end)
2319 raise error.RevlogError(m) 2463 raise error.RevlogError(m)
2320 2464
2321 comp_segment = self._segmentfile_sidedata.read_chunk( 2465 comp_segment = self._inner._segmentfile_sidedata.read_chunk(
2322 sidedata_offset, sidedata_size 2466 sidedata_offset, sidedata_size
2323 ) 2467 )
2324 2468
2325 comp = self.index[rev][11] 2469 comp = self.index[rev][11]
2326 if comp == COMP_MODE_PLAIN: 2470 if comp == COMP_MODE_PLAIN:
2421 if troffset: 2565 if troffset:
2422 tr.addbackup(self._indexfile, for_offset=True) 2566 tr.addbackup(self._indexfile, for_offset=True)
2423 tr.add(self._datafile, 0) 2567 tr.add(self._datafile, 0)
2424 2568
2425 existing_handles = False 2569 existing_handles = False
2426 if self._writinghandles is not None: 2570 if self._inner._writinghandles is not None:
2427 existing_handles = True 2571 existing_handles = True
2428 fp = self._writinghandles[0] 2572 fp = self._inner._writinghandles[0]
2429 fp.flush() 2573 fp.flush()
2430 fp.close() 2574 fp.close()
2431 # We can't use the cached file handle after close(). So prevent 2575 # We can't use the cached file handle after close(). So prevent
2432 # its usage. 2576 # its usage.
2433 self._writinghandles = None 2577 self._inner._writinghandles = None
2434 self._segmentfile.writing_handle = None 2578 self._inner._segmentfile.writing_handle = None
2435 # No need to deal with sidedata writing handle as it is only 2579 # No need to deal with sidedata writing handle as it is only
2436 # relevant with revlog-v2 which is never inline, not reaching 2580 # relevant with revlog-v2 which is never inline, not reaching
2437 # this code 2581 # this code
2438 if side_write: 2582 if side_write:
2439 old_index_file_path = self._indexfile 2583 old_index_file_path = self._indexfile
2449 checkambig=True, 2593 checkambig=True,
2450 ) 2594 )
2451 maybe_self = weak_self() 2595 maybe_self = weak_self()
2452 if maybe_self is not None: 2596 if maybe_self is not None:
2453 maybe_self._indexfile = old_index_file_path 2597 maybe_self._indexfile = old_index_file_path
2598 maybe_self._inner.index_file = maybe_self._indexfile
2454 2599
2455 def abort_callback(tr): 2600 def abort_callback(tr):
2456 maybe_self = weak_self() 2601 maybe_self = weak_self()
2457 if maybe_self is not None: 2602 if maybe_self is not None:
2458 maybe_self._indexfile = old_index_file_path 2603 maybe_self._indexfile = old_index_file_path
2604 maybe_self._inner.index_file = old_index_file_path
2459 2605
2460 tr.registertmp(new_index_file_path) 2606 tr.registertmp(new_index_file_path)
2461 if self.target[1] is not None: 2607 if self.target[1] is not None:
2462 callback_id = b'000-revlog-split-%d-%s' % self.target 2608 callback_id = b'000-revlog-split-%d-%s' % self.target
2463 else: 2609 else:
2473 new_dfh.write(self._getsegmentforrevs(r, r)[1]) 2619 new_dfh.write(self._getsegmentforrevs(r, r)[1])
2474 new_dfh.flush() 2620 new_dfh.flush()
2475 2621
2476 if side_write: 2622 if side_write:
2477 self._indexfile = new_index_file_path 2623 self._indexfile = new_index_file_path
2478 with self.__index_new_fp() as fp: 2624 self._inner.index_file = self._indexfile
2625 with self._inner._InnerRevlog__index_new_fp() as fp:
2479 self._format_flags &= ~FLAG_INLINE_DATA 2626 self._format_flags &= ~FLAG_INLINE_DATA
2480 self._inline = False 2627 self._inline = False
2628 self._inner.inline = False
2481 for i in self: 2629 for i in self:
2482 e = self.index.entry_binary(i) 2630 e = self.index.entry_binary(i)
2483 if i == 0 and self._docket is None: 2631 if i == 0 and self._docket is None:
2484 header = self._format_flags | self._format_version 2632 header = self._format_flags | self._format_version
2485 header = self.index.pack_header(header) 2633 header = self.index.pack_header(header)
2490 2638
2491 # If we don't use side-write, the temp file replace the real 2639 # If we don't use side-write, the temp file replace the real
2492 # index when we exit the context manager 2640 # index when we exit the context manager
2493 2641
2494 nodemaputil.setup_persistent_nodemap(tr, self) 2642 nodemaputil.setup_persistent_nodemap(tr, self)
2495 self._segmentfile = randomaccessfile.randomaccessfile( 2643 self._inner._segmentfile = randomaccessfile.randomaccessfile(
2496 self.opener, 2644 self.opener,
2497 self._datafile, 2645 self._datafile,
2498 self.data_config.chunk_cache_size, 2646 self.data_config.chunk_cache_size,
2499 ) 2647 )
2500 2648
2501 if existing_handles: 2649 if existing_handles:
2502 # switched from inline to conventional reopen the index 2650 # switched from inline to conventional reopen the index
2503 ifh = self.__index_write_fp() 2651 index_end = None
2504 self._writinghandles = (ifh, new_dfh, None) 2652 if self._docket is not None:
2505 self._segmentfile.writing_handle = new_dfh 2653 index_end = self._docket.index_end
2654 ifh = self._inner._InnerRevlog__index_write_fp(
2655 index_end=index_end
2656 )
2657 self._inner._writinghandles = (ifh, new_dfh, None)
2658 self._inner._segmentfile.writing_handle = new_dfh
2506 new_dfh = None 2659 new_dfh = None
2507 # No need to deal with sidedata writing handle as it is only 2660 # No need to deal with sidedata writing handle as it is only
2508 # relevant with revlog-v2 which is never inline, not reaching 2661 # relevant with revlog-v2 which is never inline, not reaching
2509 # this code 2662 # this code
2510 finally: 2663 finally:
2514 def _nodeduplicatecallback(self, transaction, node): 2667 def _nodeduplicatecallback(self, transaction, node):
2515 """called when trying to add a node already stored.""" 2668 """called when trying to add a node already stored."""
2516 2669
2517 @contextlib.contextmanager 2670 @contextlib.contextmanager
2518 def reading(self): 2671 def reading(self):
2519 """Context manager that keeps data and sidedata files open for reading""" 2672 with self._inner.reading():
2520 if len(self.index) == 0: 2673 yield
2521 yield # nothing to be read
2522 else:
2523 with self._segmentfile.reading():
2524 with self._segmentfile_sidedata.reading():
2525 yield
2526 2674
2527 @contextlib.contextmanager 2675 @contextlib.contextmanager
2528 def _writing(self, transaction): 2676 def _writing(self, transaction):
2529 if self._trypending: 2677 if self._trypending:
2530 msg = b'try to write in a `trypending` revlog: %s' 2678 msg = b'try to write in a `trypending` revlog: %s'
2531 msg %= self.display_id 2679 msg %= self.display_id
2532 raise error.ProgrammingError(msg) 2680 raise error.ProgrammingError(msg)
2533 if self._writinghandles is not None: 2681 if self._inner.is_writing:
2534 yield 2682 yield
2535 else: 2683 else:
2536 ifh = dfh = sdfh = None 2684 data_end = None
2537 try: 2685 sidedata_end = None
2538 r = len(self) 2686 if self._docket is not None:
2539 # opening the data file. 2687 data_end = self._docket.data_end
2540 dsize = 0 2688 sidedata_end = self._docket.sidedata_end
2541 if r: 2689 with self._inner.writing(
2542 dsize = self.end(r - 1) 2690 transaction,
2543 dfh = None 2691 data_end=data_end,
2544 if not self._inline: 2692 sidedata_end=sidedata_end,
2545 try: 2693 ):
2546 dfh = self._datafp(b"r+")
2547 if self._docket is None:
2548 dfh.seek(0, os.SEEK_END)
2549 else:
2550 dfh.seek(self._docket.data_end, os.SEEK_SET)
2551 except FileNotFoundError:
2552 dfh = self._datafp(b"w+")
2553 transaction.add(self._datafile, dsize)
2554 if self._sidedatafile is not None:
2555 # revlog-v2 does not inline, help Pytype
2556 assert dfh is not None
2557 try:
2558 sdfh = self.opener(self._sidedatafile, mode=b"r+")
2559 dfh.seek(self._docket.sidedata_end, os.SEEK_SET)
2560 except FileNotFoundError:
2561 sdfh = self.opener(self._sidedatafile, mode=b"w+")
2562 transaction.add(
2563 self._sidedatafile, self._docket.sidedata_end
2564 )
2565
2566 # opening the index file.
2567 isize = r * self.index.entry_size
2568 ifh = self.__index_write_fp()
2569 if self._inline:
2570 transaction.add(self._indexfile, dsize + isize)
2571 else:
2572 transaction.add(self._indexfile, isize)
2573 # exposing all file handle for writing.
2574 self._writinghandles = (ifh, dfh, sdfh)
2575 self._segmentfile.writing_handle = ifh if self._inline else dfh
2576 self._segmentfile_sidedata.writing_handle = sdfh
2577 yield 2694 yield
2578 if self._docket is not None: 2695 if self._docket is not None:
2579 self._write_docket(transaction) 2696 self._write_docket(transaction)
2580 finally:
2581 self._writinghandles = None
2582 self._segmentfile.writing_handle = None
2583 self._segmentfile_sidedata.writing_handle = None
2584 if dfh is not None:
2585 dfh.close()
2586 if sdfh is not None:
2587 sdfh.close()
2588 # closing the index file last to avoid exposing referent to
2589 # potential unflushed data content.
2590 if ifh is not None:
2591 ifh.close()
2592 2697
2593 def _write_docket(self, transaction): 2698 def _write_docket(self, transaction):
2594 """write the current docket on disk 2699 """write the current docket on disk
2595 2700
2596 Exist as a method to help changelog to implement transaction logic 2701 Exist as a method to help changelog to implement transaction logic
2809 or node in self.nodeconstants.wdirfilenodeids 2914 or node in self.nodeconstants.wdirfilenodeids
2810 ): 2915 ):
2811 raise error.RevlogError( 2916 raise error.RevlogError(
2812 _(b"%s: attempt to add wdir revision") % self.display_id 2917 _(b"%s: attempt to add wdir revision") % self.display_id
2813 ) 2918 )
2814 if self._writinghandles is None: 2919 if self._inner._writinghandles is None:
2815 msg = b'adding revision outside `revlog._writing` context' 2920 msg = b'adding revision outside `revlog._writing` context'
2816 raise error.ProgrammingError(msg) 2921 raise error.ProgrammingError(msg)
2817 2922
2818 btext = [rawtext] 2923 btext = [rawtext]
2819 2924
2821 prev = curr - 1 2926 prev = curr - 1
2822 2927
2823 offset = self._get_data_offset(prev) 2928 offset = self._get_data_offset(prev)
2824 2929
2825 if self._concurrencychecker: 2930 if self._concurrencychecker:
2826 ifh, dfh, sdfh = self._writinghandles 2931 ifh, dfh, sdfh = self._inner._writinghandles
2827 # XXX no checking for the sidedata file 2932 # XXX no checking for the sidedata file
2828 if self._inline: 2933 if self._inline:
2829 # offset is "as if" it were in the .d file, so we need to add on 2934 # offset is "as if" it were in the .d file, so we need to add on
2830 # the size of the entry metadata. 2935 # the size of the entry metadata.
2831 self._concurrencychecker( 2936 self._concurrencychecker(
3005 # 3110 #
3006 # We work around this issue by inserting a seek() before writing. 3111 # We work around this issue by inserting a seek() before writing.
3007 # Note: This is likely not necessary on Python 3. However, because 3112 # Note: This is likely not necessary on Python 3. However, because
3008 # the file handle is reused for reads and may be seeked there, we need 3113 # the file handle is reused for reads and may be seeked there, we need
3009 # to be careful before changing this. 3114 # to be careful before changing this.
3010 if self._writinghandles is None: 3115 if self._inner._writinghandles is None:
3011 msg = b'adding revision outside `revlog._writing` context' 3116 msg = b'adding revision outside `revlog._writing` context'
3012 raise error.ProgrammingError(msg) 3117 raise error.ProgrammingError(msg)
3013 ifh, dfh, sdfh = self._writinghandles 3118 ifh, dfh, sdfh = self._inner._writinghandles
3014 if self._docket is None: 3119 if self._docket is None:
3015 ifh.seek(0, os.SEEK_END) 3120 ifh.seek(0, os.SEEK_END)
3016 else: 3121 else:
3017 ifh.seek(self._docket.index_end, os.SEEK_SET) 3122 ifh.seek(self._docket.index_end, os.SEEK_SET)
3018 if dfh: 3123 if dfh:
3043 ifh.write(data[1]) 3148 ifh.write(data[1])
3044 assert not sidedata 3149 assert not sidedata
3045 self._enforceinlinesize(transaction) 3150 self._enforceinlinesize(transaction)
3046 if self._docket is not None: 3151 if self._docket is not None:
3047 # revlog-v2 always has 3 writing handles, help Pytype 3152 # revlog-v2 always has 3 writing handles, help Pytype
3048 wh1 = self._writinghandles[0] 3153 wh1 = self._inner._writinghandles[0]
3049 wh2 = self._writinghandles[1] 3154 wh2 = self._inner._writinghandles[1]
3050 wh3 = self._writinghandles[2] 3155 wh3 = self._inner._writinghandles[2]
3051 assert wh1 is not None 3156 assert wh1 is not None
3052 assert wh2 is not None 3157 assert wh2 is not None
3053 assert wh3 is not None 3158 assert wh3 is not None
3054 self._docket.index_end = wh1.tell() 3159 self._docket.index_end = wh1.tell()
3055 self._docket.data_end = wh2.tell() 3160 self._docket.data_end = wh2.tell()
3257 self._docket.write(transaction, stripping=True) 3362 self._docket.write(transaction, stripping=True)
3258 3363
3259 # then reset internal state in memory to forget those revisions 3364 # then reset internal state in memory to forget those revisions
3260 self._revisioncache = None 3365 self._revisioncache = None
3261 self._chaininfocache = util.lrucachedict(500) 3366 self._chaininfocache = util.lrucachedict(500)
3262 self._segmentfile.clear_cache() 3367 self._inner._segmentfile.clear_cache()
3263 self._segmentfile_sidedata.clear_cache() 3368 self._inner._segmentfile_sidedata.clear_cache()
3264 3369
3265 del self.index[rev:-1] 3370 del self.index[rev:-1]
3266 3371
3267 def checksize(self): 3372 def checksize(self):
3268 """Check size of index and data files 3373 """Check size of index and data files
3721 return 3826 return
3722 3827
3723 new_entries = [] 3828 new_entries = []
3724 # append the new sidedata 3829 # append the new sidedata
3725 with self._writing(transaction): 3830 with self._writing(transaction):
3726 ifh, dfh, sdfh = self._writinghandles 3831 ifh, dfh, sdfh = self._inner._writinghandles
3727 dfh.seek(self._docket.sidedata_end, os.SEEK_SET) 3832 dfh.seek(self._docket.sidedata_end, os.SEEK_SET)
3728 3833
3729 current_offset = sdfh.tell() 3834 current_offset = sdfh.tell()
3730 for rev in range(startrev, endrev + 1): 3835 for rev in range(startrev, endrev + 1):
3731 entry = self.index[rev] 3836 entry = self.index[rev]