copies: Keep changelog sidedata file open during copy tracing
Instead of having a callback that opens and closes that file many times,
a add and use a context manager method on the `revlog` class that keeps
files open for its duration.
Differential Revision: https://phab.mercurial-scm.org/D10888
--- a/mercurial/copies.py Tue Jun 22 13:18:29 2021 -0400
+++ b/mercurial/copies.py Thu Jun 17 19:48:25 2021 +0200
@@ -318,15 +318,16 @@
if p in children_count:
children_count[p] += 1
revinfo = _revinfo_getter(repo, match)
- return _combine_changeset_copies(
- revs,
- children_count,
- b.rev(),
- revinfo,
- match,
- isancestor,
- multi_thread,
- )
+ with repo.changelog.reading():
+ return _combine_changeset_copies(
+ revs,
+ children_count,
+ b.rev(),
+ revinfo,
+ match,
+ isancestor,
+ multi_thread,
+ )
else:
# When not using side-data, we will process the edges "from" the parent.
# so we need a full mapping of the parent -> children relation.
--- a/mercurial/revlog.py Tue Jun 22 13:18:29 2021 -0400
+++ b/mercurial/revlog.py Thu Jun 17 19:48:25 2021 +0200
@@ -2086,6 +2086,13 @@
"""called when trying to add a node already stored."""
@contextlib.contextmanager
+ def reading(self):
+ """Context manager that keeps data and sidedata files open for reading"""
+ with self._segmentfile.reading():
+ with self._segmentfile_sidedata.reading():
+ yield
+
+ @contextlib.contextmanager
def _writing(self, transaction):
if self._trypending:
msg = b'try to write in a `trypending` revlog: %s'
--- a/mercurial/revlogutils/randomaccessfile.py Tue Jun 22 13:18:29 2021 -0400
+++ b/mercurial/revlogutils/randomaccessfile.py Thu Jun 17 19:48:25 2021 +0200
@@ -40,6 +40,7 @@
self.filename = filename
self.default_cached_chunk_size = default_cached_chunk_size
self.writing_handle = None # This is set from revlog.py
+ self.reading_handle = None
self._cached_chunk = b''
self._cached_chunk_position = 0 # Offset from the start of the file
if initial_cache:
@@ -67,11 +68,31 @@
elif self.writing_handle:
yield self.writing_handle
+ elif self.reading_handle:
+ yield self.reading_handle
+
# Otherwise open a new file handle.
else:
with self._open() as fp:
yield fp
+ @contextlib.contextmanager
+ def reading(self):
+ """Context manager that keeps the file open for reading"""
+ if (
+ self.reading_handle is None
+ and self.writing_handle is None
+ and self.filename is not None
+ ):
+ with self._open() as fp:
+ self.reading_handle = fp
+ try:
+ yield
+ finally:
+ self.reading_handle = None
+ else:
+ yield
+
def read_chunk(self, offset, length, existing_file_obj=None):
"""Read a chunk of bytes from the file.