Mercurial > public > mercurial-scm > hg
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] |