Mercurial > hg
diff mercurial/changelog.py @ 51106:d83d788590a8
changelog-delay: move the delay/divert logic inside the (inner) revlog
Instead of hacking throught the vfs/opener, we implement the delay/divert logic
inside the `_InnerRevlog` and `randomaccessfile` object. This will allow to an
alternative implementation of the `_InnerRevlog` that does not need to use Python details.
As a result, the new implementation can use the transaction less agressively
and avoid some extra output since no data had been written yet. That seems like
a good side effect.
author | Pierre-Yves David <pierre-yves.david@octobus.net> |
---|---|
date | Tue, 24 Oct 2023 11:08:49 +0200 |
parents | 1c0f3994d733 |
children | dcaa2df1f688 |
line wrap: on
line diff
--- a/mercurial/changelog.py Thu Oct 26 05:37:37 2023 +0200 +++ b/mercurial/changelog.py Tue Oct 24 11:08:49 2023 +0200 @@ -27,7 +27,6 @@ from .revlogutils import ( constants as revlog_constants, flagutil, - randomaccessfile, ) _defaultextra = {b'branch': b'default'} @@ -92,38 +91,6 @@ return b'\n'.join([l.rstrip() for l in desc.splitlines()]).strip(b'\n') -class _divertopener: - def __init__(self, opener, target): - self._opener = opener - self._target = target - - def __call__(self, name, mode=b'r', checkambig=False, **kwargs): - if name != self._target: - return self._opener(name, mode, **kwargs) - return self._opener(name + b".a", mode, **kwargs) - - def __getattr__(self, attr): - return getattr(self._opener, attr) - - -class _delayopener: - """build an opener that stores chunks in 'buf' instead of 'target'""" - - def __init__(self, opener, target, buf): - self._opener = opener - self._target = target - self._buf = buf - - def __call__(self, name, mode=b'r', checkambig=False, **kwargs): - if name != self._target: - return self._opener(name, mode, **kwargs) - assert not kwargs - return randomaccessfile.appender(self._opener, name, mode, self._buf) - - def __getattr__(self, attr): - return getattr(self._opener, attr) - - @attr.s class _changelogrevision: # Extensions might modify _defaultextra, so let the constructor below pass @@ -354,10 +321,7 @@ # chains. self._storedeltachains = False - self._realopener = opener - self._delayed = False - self._delaybuf = None - self._divert = False + self._v2_delayed = False self._filteredrevs = frozenset() self._filteredrevs_hashcache = {} self._copiesstorage = opener.options.get(b'copies-storage') @@ -374,90 +338,47 @@ self._filteredrevs_hashcache = {} def _write_docket(self, tr): - if not self.is_delaying: + if not self._v2_delayed: super(changelog, self)._write_docket(tr) - @property - def is_delaying(self): - return self._delayed - def delayupdate(self, tr): """delay visibility of index updates to other readers""" assert not self._inner.is_open - if self._docket is None and not self.is_delaying: - if len(self) == 0: - self._divert = True - if self._realopener.exists(self._indexfile + b'.a'): - self._realopener.unlink(self._indexfile + b'.a') - self.opener = _divertopener(self._realopener, self._indexfile) - else: - self._delaybuf = [] - self.opener = _delayopener( - self._realopener, self._indexfile, self._delaybuf - ) - self._inner.opener = self.opener - self._inner._segmentfile.opener = self.opener - self._inner._segmentfile_sidedata.opener = self.opener - self._delayed = True + if self._docket is not None: + self._v2_delayed = True + else: + new_index = self._inner.delay() + if new_index is not None: + self._indexfile = new_index + tr.registertmp(new_index) tr.addpending(b'cl-%i' % id(self), self._writepending) tr.addfinalize(b'cl-%i' % id(self), self._finalize) def _finalize(self, tr): """finalize index updates""" assert not self._inner.is_open - self._delayed = False - self.opener = self._realopener - self._inner.opener = self.opener - self._inner._segmentfile.opener = self.opener - self._inner._segmentfile_sidedata.opener = self.opener - # move redirected index data back into place if self._docket is not None: - self._write_docket(tr) - elif self._divert: - assert not self._delaybuf - tmpname = self._indexfile + b".a" - nfile = self.opener.open(tmpname) - nfile.close() - self.opener.rename(tmpname, self._indexfile, checkambig=True) - elif self._delaybuf: - fp = self.opener(self._indexfile, b'a', checkambig=True) - fp.write(b"".join(self._delaybuf)) - fp.close() - self._delaybuf = None - self._divert = False - # split when we're done - self._enforceinlinesize(tr, side_write=False) + self._docket.write(tr) + self._v2_delayed = False + else: + new_index_file = self._inner.finalize_pending() + self._indexfile = new_index_file + # split when we're done + self._enforceinlinesize(tr, side_write=False) def _writepending(self, tr): """create a file containing the unfinalized state for pretxnchangegroup""" assert not self._inner.is_open if self._docket: - return self._docket.write(tr, pending=True) - if self._delaybuf: - # make a temporary copy of the index - fp1 = self._realopener(self._indexfile) - pendingfilename = self._indexfile + b".a" - # register as a temp file to ensure cleanup on failure - tr.registertmp(pendingfilename) - # write existing data - fp2 = self._realopener(pendingfilename, b"w") - fp2.write(fp1.read()) - # add pending data - fp2.write(b"".join(self._delaybuf)) - fp2.close() - # switch modes so finalize can simply rename - self._delaybuf = None - self._divert = True - self.opener = _divertopener(self._realopener, self._indexfile) - self._inner.opener = self.opener - self._inner._segmentfile.opener = self.opener - self._inner._segmentfile_sidedata.opener = self.opener - - if self._divert: - return True - - return False + any_pending = self._docket.write(tr, pending=True) + self._v2_delayed = False + else: + new_index, any_pending = self._inner.write_pending() + if new_index is not None: + self._indexfile = new_index + tr.registertmp(new_index) + return any_pending def _enforceinlinesize(self, tr, side_write=True): if not self.is_delaying: