A future patch will need to address the issue of Rust module policy,
to avoid having ugly duplicate imports and conditionals all over the place.
As the rewrite of dirstate in Rust progresses, we will need fewer of those
"contact points".
hg-reviewers |
A future patch will need to address the issue of Rust module policy,
to avoid having ugly duplicate imports and conditionals all over the place.
As the rewrite of dirstate in Rust progresses, we will need fewer of those
"contact points".
Automatic diff as part of commit; lint not applicable. |
Automatic diff as part of commit; unit tests not applicable. |
Path | Packages | |||
---|---|---|---|---|
M | mercurial/dirstate.py (20 lines) | |||
M | tests/fakedirstatewritetime.py (18 lines) |
pathutil, | pathutil, | ||||
policy, | policy, | ||||
pycompat, | pycompat, | ||||
scmutil, | scmutil, | ||||
txnutil, | txnutil, | ||||
util, | util, | ||||
) | ) | ||||
try: | |||||
from . import rustext | |||||
rustext.__name__ # force actual import (see hgdemandimport) | |||||
except ImportError: | |||||
rustext = None | |||||
parsers = policy.importmod(r'parsers') | parsers = policy.importmod(r'parsers') | ||||
propertycache = util.propertycache | propertycache = util.propertycache | ||||
filecache = scmutil.filecache | filecache = scmutil.filecache | ||||
_rangemask = 0x7fffffff | _rangemask = 0x7fffffff | ||||
dirstatetuple = parsers.dirstatetuple | dirstatetuple = parsers.dirstatetuple | ||||
# them as not to be tracked by the collector. However, this has no | # them as not to be tracked by the collector. However, this has no | ||||
# effect on when GCs are triggered, only on what objects the GC looks | # effect on when GCs are triggered, only on what objects the GC looks | ||||
# into. This means that O(number of files) GCs are unavoidable. | # into. This means that O(number of files) GCs are unavoidable. | ||||
# Depending on when in the process's lifetime the dirstate is parsed, | # Depending on when in the process's lifetime the dirstate is parsed, | ||||
# this can get very expensive. As a workaround, disable GC while | # this can get very expensive. As a workaround, disable GC while | ||||
# parsing the dirstate. | # parsing the dirstate. | ||||
# | # | ||||
# (we cannot decorate the function directly since it is in a C module) | # (we cannot decorate the function directly since it is in a C module) | ||||
parse_dirstate = util.nogc(parsers.parse_dirstate) | if rustext is not None: | ||||
parse_dirstate = rustext.dirstate.parse_dirstate | |||||
else: | |||||
parse_dirstate = parsers.parse_dirstate | |||||
parse_dirstate = util.nogc(parse_dirstate) | |||||
p = parse_dirstate(self._map, self.copymap, st) | p = parse_dirstate(self._map, self.copymap, st) | ||||
if not self._dirtyparents: | if not self._dirtyparents: | ||||
self.setparents(*p) | self.setparents(*p) | ||||
# Avoid excess attribute lookups by fast pathing certain checks | # Avoid excess attribute lookups by fast pathing certain checks | ||||
self.__contains__ = self._map.__contains__ | self.__contains__ = self._map.__contains__ | ||||
self.__getitem__ = self._map.__getitem__ | self.__getitem__ = self._map.__getitem__ | ||||
self.get = self._map.get | self.get = self._map.get | ||||
def write(self, st, now): | def write(self, st, now): | ||||
st.write(parsers.pack_dirstate(self._map, self.copymap, | if rustext is not None: | ||||
pack_dirstate = rustext.dirstate.pack_dirstate | |||||
else: | |||||
pack_dirstate = parsers.pack_dirstate | |||||
st.write(pack_dirstate(self._map, self.copymap, | |||||
self.parents(), now)) | self.parents(), now)) | ||||
st.close() | st.close() | ||||
self._dirtyparents = False | self._dirtyparents = False | ||||
self.nonnormalset, self.otherparentset = self.nonnormalentries() | self.nonnormalset, self.otherparentset = self.nonnormalentries() | ||||
@propertycache | @propertycache | ||||
def nonnormalset(self): | def nonnormalset(self): | ||||
nonnorm, otherparents = self.nonnormalentries() | nonnorm, otherparents = self.nonnormalentries() |
context, | context, | ||||
dirstate, | dirstate, | ||||
extensions, | extensions, | ||||
policy, | policy, | ||||
registrar, | registrar, | ||||
) | ) | ||||
from mercurial.utils import dateutil | from mercurial.utils import dateutil | ||||
try: | |||||
from mercurial import rustext | |||||
rustext.__name__ # force actual import (see hgdemandimport) | |||||
except ImportError: | |||||
rustext = None | |||||
configtable = {} | configtable = {} | ||||
configitem = registrar.configitem(configtable) | configitem = registrar.configitem(configtable) | ||||
configitem(b'fakedirstatewritetime', b'fakenow', | configitem(b'fakedirstatewritetime', b'fakenow', | ||||
default=None, | default=None, | ||||
) | ) | ||||
parsers = policy.importmod(r'parsers') | parsers = policy.importmod(r'parsers') | ||||
# because replacing 'parsers.pack_dirstate' is also effective | # because replacing 'parsers.pack_dirstate' is also effective | ||||
# in subrepos. | # in subrepos. | ||||
return func() | return func() | ||||
# parsing 'fakenow' in YYYYmmddHHMM format makes comparison between | # parsing 'fakenow' in YYYYmmddHHMM format makes comparison between | ||||
# 'fakenow' value and 'touch -t YYYYmmddHHMM' argument easy | # 'fakenow' value and 'touch -t YYYYmmddHHMM' argument easy | ||||
fakenow = dateutil.parsedate(fakenow, [b'%Y%m%d%H%M'])[0] | fakenow = dateutil.parsedate(fakenow, [b'%Y%m%d%H%M'])[0] | ||||
if rustext is not None: | |||||
orig_module = rustext.dirstate | |||||
orig_pack_dirstate = rustext.dirstate.pack_dirstate | |||||
else: | |||||
orig_module = parsers | |||||
orig_pack_dirstate = parsers.pack_dirstate | orig_pack_dirstate = parsers.pack_dirstate | ||||
orig_dirstate_getfsnow = dirstate._getfsnow | orig_dirstate_getfsnow = dirstate._getfsnow | ||||
wrapper = lambda *args: pack_dirstate(fakenow, orig_pack_dirstate, *args) | wrapper = lambda *args: pack_dirstate(fakenow, orig_pack_dirstate, *args) | ||||
parsers.pack_dirstate = wrapper | orig_module.pack_dirstate = wrapper | ||||
dirstate._getfsnow = lambda *args: fakenow | dirstate._getfsnow = lambda *args: fakenow | ||||
try: | try: | ||||
return func() | return func() | ||||
finally: | finally: | ||||
parsers.pack_dirstate = orig_pack_dirstate | orig_module.pack_dirstate = orig_pack_dirstate | ||||
dirstate._getfsnow = orig_dirstate_getfsnow | dirstate._getfsnow = orig_dirstate_getfsnow | ||||
def _poststatusfixup(orig, workingctx, status, fixup): | def _poststatusfixup(orig, workingctx, status, fixup): | ||||
ui = workingctx.repo().ui | ui = workingctx.repo().ui | ||||
return fakewrite(ui, lambda : orig(workingctx, status, fixup)) | return fakewrite(ui, lambda : orig(workingctx, status, fixup)) | ||||
def markcommitted(orig, committablectx, node): | def markcommitted(orig, committablectx, node): | ||||
ui = committablectx.repo().ui | ui = committablectx.repo().ui | ||||
return fakewrite(ui, lambda : orig(committablectx, node)) | return fakewrite(ui, lambda : orig(committablectx, node)) | ||||
def extsetup(ui): | def extsetup(ui): | ||||
extensions.wrapfunction(context.workingctx, '_poststatusfixup', | extensions.wrapfunction(context.workingctx, '_poststatusfixup', | ||||
_poststatusfixup) | _poststatusfixup) | ||||
extensions.wrapfunction(context.workingctx, 'markcommitted', | extensions.wrapfunction(context.workingctx, 'markcommitted', | ||||
markcommitted) | markcommitted) |