Read about our upcoming Code of Conduct on this issue

Commit 4eed8466 authored by Simon Sapin's avatar Simon Sapin
Browse files

dirstate-v2: Add support when Rust is not enabled

This wires into `dirstatemap` the parser and serializer added in previous
changesets. The memory representation is still the same, with a flat `dict`
for `DirstateItem`s and another one for copy sources. Serialization always
creates a new dirstate-v2 data file and does not support (when Rust is not
enabled) appending to an existing one, since we don’t keep track of which
tree nodes are new or modified. Instead the tree is reconstructed during
serialization.

Differential Revision: https://phab.mercurial-scm.org/D11520
parent 7e78c72ee3ea
Pipeline #27753 failed with stages
in 52 minutes and 31 seconds
...@@ -39,8 +39,6 @@ from .interfaces import ( ...@@ -39,8 +39,6 @@ from .interfaces import (
parsers = policy.importmod('parsers') parsers = policy.importmod('parsers')
rustmod = policy.importrust('dirstate') rustmod = policy.importrust('dirstate')
SUPPORTS_DIRSTATE_V2 = rustmod is not None
propertycache = util.propertycache propertycache = util.propertycache
filecache = scmutil.filecache filecache = scmutil.filecache
_rangemask = dirstatemap.rangemask _rangemask = dirstatemap.rangemask
......
...@@ -332,15 +332,6 @@ class dirstatemap(_dirstatemapcommon): ...@@ -332,15 +332,6 @@ class dirstatemap(_dirstatemapcommon):
denormalized form that they appear as in the dirstate. denormalized form that they appear as in the dirstate.
""" """
def __init__(self, ui, opener, root, nodeconstants, use_dirstate_v2):
super(dirstatemap, self).__init__(
ui, opener, root, nodeconstants, use_dirstate_v2
)
if self._use_dirstate_v2:
msg = "Dirstate V2 not supportedi"
msg += "(should have detected unsupported requirement)"
raise error.ProgrammingError(msg)
### Core data storage and access ### Core data storage and access
@propertycache @propertycache
...@@ -406,19 +397,17 @@ class dirstatemap(_dirstatemapcommon): ...@@ -406,19 +397,17 @@ class dirstatemap(_dirstatemapcommon):
self._opener.join(self._filename) self._opener.join(self._filename)
) )
try: if self._use_dirstate_v2:
fp = self._opendirstatefile() if not self.docket.uuid:
try: return
st = fp.read() st = self._opener.read(self.docket.data_filename())
finally: else:
fp.close() st = self._readdirstatefile()
except IOError as err:
if err.errno != errno.ENOENT:
raise
return
if not st: if not st:
return return
# TODO: adjust this estimate for dirstate-v2
if util.safehasattr(parsers, b'dict_new_presized'): if util.safehasattr(parsers, b'dict_new_presized'):
# Make an estimate of the number of files in the dirstate based on # Make an estimate of the number of files in the dirstate based on
# its size. This trades wasting some memory for avoiding costly # its size. This trades wasting some memory for avoiding costly
...@@ -440,8 +429,14 @@ class dirstatemap(_dirstatemapcommon): ...@@ -440,8 +429,14 @@ class dirstatemap(_dirstatemapcommon):
# 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 self._use_dirstate_v2:
p = parse_dirstate(self._map, self.copymap, st) p = self.docket.parents
meta = self.docket.tree_metadata
parse_dirstate = util.nogc(v2.parse_dirstate)
parse_dirstate(self._map, self.copymap, st, meta)
else:
parse_dirstate = util.nogc(parsers.parse_dirstate)
p = parse_dirstate(self._map, self.copymap, st)
if not self._dirtyparents: if not self._dirtyparents:
self.setparents(*p) self.setparents(*p)
...@@ -450,10 +445,16 @@ class dirstatemap(_dirstatemapcommon): ...@@ -450,10 +445,16 @@ class dirstatemap(_dirstatemapcommon):
self.__getitem__ = self._map.__getitem__ self.__getitem__ = self._map.__getitem__
self.get = self._map.get self.get = self._map.get
def write(self, _tr, st, now): def write(self, tr, st, now):
d = parsers.pack_dirstate(self._map, self.copymap, self.parents(), now) if self._use_dirstate_v2:
st.write(d) packed, meta = v2.pack_dirstate(self._map, self.copymap, now)
st.close() self.write_v2_no_append(tr, st, meta, packed)
else:
packed = parsers.pack_dirstate(
self._map, self.copymap, self.parents(), now
)
st.write(packed)
st.close()
self._dirtyparents = False self._dirtyparents = False
@propertycache @propertycache
......
...@@ -917,9 +917,6 @@ def gathersupportedrequirements(ui): ...@@ -917,9 +917,6 @@ def gathersupportedrequirements(ui):
# Start with all requirements supported by this file. # Start with all requirements supported by this file.
supported = set(localrepository._basesupported) supported = set(localrepository._basesupported)
if dirstate.SUPPORTS_DIRSTATE_V2:
supported.add(requirementsmod.DIRSTATE_V2_REQUIREMENT)
# Execute ``featuresetupfuncs`` entries if they belong to an extension # Execute ``featuresetupfuncs`` entries if they belong to an extension
# relevant to this ui instance. # relevant to this ui instance.
modules = {m.__name__ for n, m in extensions.extensions(ui)} modules = {m.__name__ for n, m in extensions.extensions(ui)}
...@@ -1266,6 +1263,7 @@ class localrepository(object): ...@@ -1266,6 +1263,7 @@ class localrepository(object):
requirementsmod.NODEMAP_REQUIREMENT, requirementsmod.NODEMAP_REQUIREMENT,
bookmarks.BOOKMARKS_IN_STORE_REQUIREMENT, bookmarks.BOOKMARKS_IN_STORE_REQUIREMENT,
requirementsmod.SHARESAFE_REQUIREMENT, requirementsmod.SHARESAFE_REQUIREMENT,
requirementsmod.DIRSTATE_V2_REQUIREMENT,
} }
_basesupported = supportedformats | { _basesupported = supportedformats | {
requirementsmod.STORE_REQUIREMENT, requirementsmod.STORE_REQUIREMENT,
...@@ -3609,15 +3607,7 @@ def newreporequirements(ui, createopts): ...@@ -3609,15 +3607,7 @@ def newreporequirements(ui, createopts):
# experimental config: format.exp-dirstate-v2 # experimental config: format.exp-dirstate-v2
# Keep this logic in sync with `has_dirstate_v2()` in `tests/hghave.py` # Keep this logic in sync with `has_dirstate_v2()` in `tests/hghave.py`
if ui.configbool(b'format', b'exp-dirstate-v2'): if ui.configbool(b'format', b'exp-dirstate-v2'):
if dirstate.SUPPORTS_DIRSTATE_V2: requirements.add(requirementsmod.DIRSTATE_V2_REQUIREMENT)
requirements.add(requirementsmod.DIRSTATE_V2_REQUIREMENT)
else:
raise error.Abort(
_(
b"dirstate v2 format requested by config "
b"but not supported (requires Rust extensions)"
)
)
# experimental config: format.exp-use-copies-side-data-changeset # experimental config: format.exp-use-copies-side-data-changeset
if ui.configbool(b'format', b'exp-use-copies-side-data-changeset'): if ui.configbool(b'format', b'exp-use-copies-side-data-changeset'):
......
#testcases dirstate-v1 dirstate-v2 #testcases dirstate-v1 dirstate-v2
#if dirstate-v2 #if dirstate-v2
#require rust
$ echo '[format]' >> $HGRCPATH $ echo '[format]' >> $HGRCPATH
$ echo 'exp-dirstate-v2=1' >> $HGRCPATH $ echo 'exp-dirstate-v2=1' >> $HGRCPATH
#endif #endif
......
#testcases dirstate-v1 dirstate-v2 #testcases dirstate-v1 dirstate-v2
#if dirstate-v2 #if dirstate-v2
#require rust
$ echo '[format]' >> $HGRCPATH $ echo '[format]' >> $HGRCPATH
$ echo 'exp-dirstate-v2=1' >> $HGRCPATH $ echo 'exp-dirstate-v2=1' >> $HGRCPATH
#endif #endif
......
#testcases dirstate-v1 dirstate-v2 #testcases dirstate-v1 dirstate-v2
#if dirstate-v2 #if dirstate-v2
#require rust
$ echo '[format]' >> $HGRCPATH $ echo '[format]' >> $HGRCPATH
$ echo 'exp-dirstate-v2=1' >> $HGRCPATH $ echo 'exp-dirstate-v2=1' >> $HGRCPATH
#endif #endif
......
#testcases dirstate-v1 dirstate-v2 #testcases dirstate-v1 dirstate-v2
#if dirstate-v2 #if dirstate-v2
#require rust
$ echo '[format]' >> $HGRCPATH $ echo '[format]' >> $HGRCPATH
$ echo 'exp-dirstate-v2=1' >> $HGRCPATH $ echo 'exp-dirstate-v2=1' >> $HGRCPATH
#endif #endif
...@@ -397,9 +396,10 @@ Windows paths are accepted on input ...@@ -397,9 +396,10 @@ Windows paths are accepted on input
#endif #endif
#if dirstate-v2 #if dirstate-v2 rust
Check the hash of ignore patterns written in the dirstate Check the hash of ignore patterns written in the dirstate
This is an optimization that is only relevant when using the Rust extensions
$ hg status > /dev/null $ hg status > /dev/null
$ cat .hg/testhgignore .hg/testhgignorerel .hgignore dir2/.hgignore dir1/.hgignore dir1/.hgignoretwo | $TESTDIR/f --sha1 $ cat .hg/testhgignore .hg/testhgignorerel .hgignore dir2/.hgignore dir1/.hgignore dir1/.hgignoretwo | $TESTDIR/f --sha1
......
...@@ -3,7 +3,6 @@ ...@@ -3,7 +3,6 @@
#testcases dirstate-v1 dirstate-v2 #testcases dirstate-v1 dirstate-v2
#if dirstate-v2 #if dirstate-v2
#require rust
$ echo '[format]' >> $HGRCPATH $ echo '[format]' >> $HGRCPATH
$ echo 'exp-dirstate-v2=1' >> $HGRCPATH $ echo 'exp-dirstate-v2=1' >> $HGRCPATH
#endif #endif
......
#testcases dirstate-v1 dirstate-v2 #testcases dirstate-v1 dirstate-v2
#if dirstate-v2 #if dirstate-v2
#require rust
$ echo '[format]' >> $HGRCPATH $ echo '[format]' >> $HGRCPATH
$ echo 'exp-dirstate-v2=1' >> $HGRCPATH $ echo 'exp-dirstate-v2=1' >> $HGRCPATH
#endif #endif
......
#testcases dirstate-v1 dirstate-v2 #testcases dirstate-v1 dirstate-v2
#if no-rust
$ hg init repo0 --config format.exp-dirstate-v2=1
abort: dirstate v2 format requested by config but not supported (requires Rust extensions)
[255]
#endif
#if dirstate-v2 #if dirstate-v2
#require rust
$ echo '[format]' >> $HGRCPATH $ echo '[format]' >> $HGRCPATH
$ echo 'exp-dirstate-v2=1' >> $HGRCPATH $ echo 'exp-dirstate-v2=1' >> $HGRCPATH
#endif #endif
...@@ -743,7 +736,7 @@ When a directory containing a tracked file gets symlinked, as of 5.8 ...@@ -743,7 +736,7 @@ When a directory containing a tracked file gets symlinked, as of 5.8
if also listing unknowns. if also listing unknowns.
The tree-based dirstate and status algorithm fix this: The tree-based dirstate and status algorithm fix this:
#if symlink no-dirstate-v1 #if symlink no-dirstate-v1 rust
$ cd .. $ cd ..
$ hg init issue6335 $ hg init issue6335
...@@ -759,11 +752,11 @@ The tree-based dirstate and status algorithm fix this: ...@@ -759,11 +752,11 @@ The tree-based dirstate and status algorithm fix this:
? bar/a ? bar/a
? foo ? foo
$ hg status -c # incorrect output with `dirstate-v1` $ hg status -c # incorrect output without the Rust implementation
$ hg status -cu $ hg status -cu
? bar/a ? bar/a
? foo ? foo
$ hg status -d # incorrect output with `dirstate-v1` $ hg status -d # incorrect output without the Rust implementation
! foo/a ! foo/a
$ hg status -du $ hg status -du
! foo/a ! foo/a
...@@ -910,7 +903,7 @@ Check using include flag while listing ignored composes correctly (issue6514) ...@@ -910,7 +903,7 @@ Check using include flag while listing ignored composes correctly (issue6514)
I B.hs I B.hs
I ignored-folder/ctest.hs I ignored-folder/ctest.hs
#if dirstate-v2 #if rust dirstate-v2
Check read_dir caching Check read_dir caching
......
...@@ -3,7 +3,6 @@ ...@@ -3,7 +3,6 @@
#testcases dirstate-v1 dirstate-v2 #testcases dirstate-v1 dirstate-v2
#if dirstate-v2 #if dirstate-v2
#require rust
$ echo '[format]' >> $HGRCPATH $ echo '[format]' >> $HGRCPATH
$ echo 'exp-dirstate-v2=1' >> $HGRCPATH $ echo 'exp-dirstate-v2=1' >> $HGRCPATH
#endif #endif
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment