##// END OF EJS Templates
dirstate-v2: Change the on-disk format when the requirement is enabled...
Simon Sapin -
r48055:1766130f default
parent child Browse files
Show More
@@ -0,0 +1,4 b''
1 /// Added at the start of `.hg/dirstate` when the "v2" format is used.
2 /// Acts like a "magic number". This is a sanity check, not strictly necessary
3 /// since `.hg/requires` already governs which format should be used.
4 pub const V2_FORMAT_MARKER: &[u8; 12] = b"dirstate-v2\n";
@@ -25,6 +25,7 b' from mercurial import ('
25 httpconnection,
25 httpconnection,
26 match as matchmod,
26 match as matchmod,
27 pycompat,
27 pycompat,
28 requirements,
28 scmutil,
29 scmutil,
29 sparse,
30 sparse,
30 util,
31 util,
@@ -197,6 +198,7 b' def openlfdirstate(ui, repo, create=True'
197 vfs = repo.vfs
198 vfs = repo.vfs
198 lfstoredir = longname
199 lfstoredir = longname
199 opener = vfsmod.vfs(vfs.join(lfstoredir))
200 opener = vfsmod.vfs(vfs.join(lfstoredir))
201 use_dirstate_v2 = requirements.DIRSTATE_V2_REQUIREMENT in repo.requirements
200 lfdirstate = largefilesdirstate(
202 lfdirstate = largefilesdirstate(
201 opener,
203 opener,
202 ui,
204 ui,
@@ -204,6 +206,7 b' def openlfdirstate(ui, repo, create=True'
204 repo.dirstate._validate,
206 repo.dirstate._validate,
205 lambda: sparse.matcher(repo),
207 lambda: sparse.matcher(repo),
206 repo.nodeconstants,
208 repo.nodeconstants,
209 use_dirstate_v2,
207 )
210 )
208
211
209 # If the largefiles dirstate does not exist, populate and create
212 # If the largefiles dirstate does not exist, populate and create
@@ -75,7 +75,14 b' def _getfsnow(vfs):'
75 @interfaceutil.implementer(intdirstate.idirstate)
75 @interfaceutil.implementer(intdirstate.idirstate)
76 class dirstate(object):
76 class dirstate(object):
77 def __init__(
77 def __init__(
78 self, opener, ui, root, validate, sparsematchfn, nodeconstants
78 self,
79 opener,
80 ui,
81 root,
82 validate,
83 sparsematchfn,
84 nodeconstants,
85 use_dirstate_v2,
79 ):
86 ):
80 """Create a new dirstate object.
87 """Create a new dirstate object.
81
88
@@ -83,6 +90,7 b' class dirstate(object):'
83 dirstate file; root is the root of the directory tracked by
90 dirstate file; root is the root of the directory tracked by
84 the dirstate.
91 the dirstate.
85 """
92 """
93 self._use_dirstate_v2 = use_dirstate_v2
86 self._nodeconstants = nodeconstants
94 self._nodeconstants = nodeconstants
87 self._opener = opener
95 self._opener = opener
88 self._validate = validate
96 self._validate = validate
@@ -141,7 +149,11 b' class dirstate(object):'
141 def _map(self):
149 def _map(self):
142 """Return the dirstate contents (see documentation for dirstatemap)."""
150 """Return the dirstate contents (see documentation for dirstatemap)."""
143 self._map = self._mapcls(
151 self._map = self._mapcls(
144 self._ui, self._opener, self._root, self._nodeconstants
152 self._ui,
153 self._opener,
154 self._root,
155 self._nodeconstants,
156 self._use_dirstate_v2,
145 )
157 )
146 return self._map
158 return self._map
147
159
@@ -1435,13 +1447,16 b' class dirstatemap(object):'
1435 denormalized form that they appear as in the dirstate.
1447 denormalized form that they appear as in the dirstate.
1436 """
1448 """
1437
1449
1438 def __init__(self, ui, opener, root, nodeconstants):
1450 def __init__(self, ui, opener, root, nodeconstants, use_dirstate_v2):
1439 self._ui = ui
1451 self._ui = ui
1440 self._opener = opener
1452 self._opener = opener
1441 self._root = root
1453 self._root = root
1442 self._filename = b'dirstate'
1454 self._filename = b'dirstate'
1443 self._nodelen = 20
1455 self._nodelen = 20
1444 self._nodeconstants = nodeconstants
1456 self._nodeconstants = nodeconstants
1457 assert (
1458 not use_dirstate_v2
1459 ), "should have detected unsupported requirement"
1445
1460
1446 self._parents = None
1461 self._parents = None
1447 self._dirtyparents = False
1462 self._dirtyparents = False
@@ -1746,13 +1761,14 b' class dirstatemap(object):'
1746 if rustmod is not None:
1761 if rustmod is not None:
1747
1762
1748 class dirstatemap(object):
1763 class dirstatemap(object):
1749 def __init__(self, ui, opener, root, nodeconstants):
1764 def __init__(self, ui, opener, root, nodeconstants, use_dirstate_v2):
1765 self._use_dirstate_v2 = use_dirstate_v2
1750 self._nodeconstants = nodeconstants
1766 self._nodeconstants = nodeconstants
1751 self._ui = ui
1767 self._ui = ui
1752 self._opener = opener
1768 self._opener = opener
1753 self._root = root
1769 self._root = root
1754 self._filename = b'dirstate'
1770 self._filename = b'dirstate'
1755 self._nodelen = 20
1771 self._nodelen = 20 # Also update Rust code when changing this!
1756 self._parents = None
1772 self._parents = None
1757 self._dirtyparents = False
1773 self._dirtyparents = False
1758
1774
@@ -1832,9 +1848,14 b' if rustmod is not None:'
1832
1848
1833 def parents(self):
1849 def parents(self):
1834 if not self._parents:
1850 if not self._parents:
1851 if self._use_dirstate_v2:
1852 offset = len(rustmod.V2_FORMAT_MARKER)
1853 else:
1854 offset = 0
1855 read_len = offset + self._nodelen * 2
1835 try:
1856 try:
1836 fp = self._opendirstatefile()
1857 fp = self._opendirstatefile()
1837 st = fp.read(40)
1858 st = fp.read(read_len)
1838 fp.close()
1859 fp.close()
1839 except IOError as err:
1860 except IOError as err:
1840 if err.errno != errno.ENOENT:
1861 if err.errno != errno.ENOENT:
@@ -1843,7 +1864,8 b' if rustmod is not None:'
1843 st = b''
1864 st = b''
1844
1865
1845 l = len(st)
1866 l = len(st)
1846 if l == self._nodelen * 2:
1867 if l == read_len:
1868 st = st[offset:]
1847 self._parents = (
1869 self._parents = (
1848 st[: self._nodelen],
1870 st[: self._nodelen],
1849 st[self._nodelen : 2 * self._nodelen],
1871 st[self._nodelen : 2 * self._nodelen],
@@ -1887,7 +1909,7 b' if rustmod is not None:'
1887 False,
1909 False,
1888 )
1910 )
1889 self._rustmap, parents = rustmod.DirstateMap.new(
1911 self._rustmap, parents = rustmod.DirstateMap.new(
1890 use_dirstate_tree, st
1912 use_dirstate_tree, self._use_dirstate_v2, st
1891 )
1913 )
1892
1914
1893 if parents and not self._dirtyparents:
1915 if parents and not self._dirtyparents:
@@ -1900,7 +1922,10 b' if rustmod is not None:'
1900
1922
1901 def write(self, st, now):
1923 def write(self, st, now):
1902 parents = self.parents()
1924 parents = self.parents()
1903 st.write(self._rustmap.write(parents[0], parents[1], now))
1925 packed = self._rustmap.write(
1926 self._use_dirstate_v2, parents[0], parents[1], now
1927 )
1928 st.write(packed)
1904 st.close()
1929 st.close()
1905 self._dirtyparents = False
1930 self._dirtyparents = False
1906
1931
@@ -6,7 +6,15 b' from . import util as interfaceutil'
6
6
7
7
8 class idirstate(interfaceutil.Interface):
8 class idirstate(interfaceutil.Interface):
9 def __init__(opener, ui, root, validate, sparsematchfn, nodeconstants):
9 def __init__(
10 opener,
11 ui,
12 root,
13 validate,
14 sparsematchfn,
15 nodeconstants,
16 use_dirstate_v2,
17 ):
10 """Create a new dirstate object.
18 """Create a new dirstate object.
11
19
12 opener is an open()-like callable that can be used to open the
20 opener is an open()-like callable that can be used to open the
@@ -1690,6 +1690,8 b' class localrepository(object):'
1690 def _makedirstate(self):
1690 def _makedirstate(self):
1691 """Extension point for wrapping the dirstate per-repo."""
1691 """Extension point for wrapping the dirstate per-repo."""
1692 sparsematchfn = lambda: sparse.matcher(self)
1692 sparsematchfn = lambda: sparse.matcher(self)
1693 v2_req = requirementsmod.DIRSTATE_V2_REQUIREMENT
1694 use_dirstate_v2 = v2_req in self.requirements
1693
1695
1694 return dirstate.dirstate(
1696 return dirstate.dirstate(
1695 self.vfs,
1697 self.vfs,
@@ -1698,6 +1700,7 b' class localrepository(object):'
1698 self._dirstatevalidate,
1700 self._dirstatevalidate,
1699 sparsematchfn,
1701 sparsematchfn,
1700 self.nodeconstants,
1702 self.nodeconstants,
1703 use_dirstate_v2,
1701 )
1704 )
1702
1705
1703 def _dirstatevalidate(self, node):
1706 def _dirstatevalidate(self, node):
@@ -1,4 +1,5 b''
1 pub mod dirstate_map;
1 pub mod dirstate_map;
2 pub mod dispatch;
2 pub mod dispatch;
3 pub mod on_disk;
3 pub mod path_with_basename;
4 pub mod path_with_basename;
4 mod status;
5 mod status;
@@ -4,14 +4,17 b' use std::borrow::Cow;'
4 use std::convert::TryInto;
4 use std::convert::TryInto;
5 use std::path::PathBuf;
5 use std::path::PathBuf;
6
6
7 use super::on_disk::V2_FORMAT_MARKER;
7 use super::path_with_basename::WithBasename;
8 use super::path_with_basename::WithBasename;
8 use crate::dirstate::parsers::clear_ambiguous_mtime;
9 use crate::dirstate::parsers::clear_ambiguous_mtime;
9 use crate::dirstate::parsers::pack_entry;
10 use crate::dirstate::parsers::pack_entry;
10 use crate::dirstate::parsers::packed_entry_size;
11 use crate::dirstate::parsers::packed_entry_size;
11 use crate::dirstate::parsers::parse_dirstate_entries;
12 use crate::dirstate::parsers::parse_dirstate_entries;
12 use crate::dirstate::parsers::Timestamp;
13 use crate::dirstate::parsers::Timestamp;
14 use crate::errors::HgError;
13 use crate::matchers::Matcher;
15 use crate::matchers::Matcher;
14 use crate::utils::hg_path::{HgPath, HgPathBuf};
16 use crate::utils::hg_path::{HgPath, HgPathBuf};
17 use crate::utils::SliceExt;
15 use crate::CopyMapIter;
18 use crate::CopyMapIter;
16 use crate::DirstateEntry;
19 use crate::DirstateEntry;
17 use crate::DirstateError;
20 use crate::DirstateError;
@@ -75,7 +78,24 b" type NodeDataMut<'tree, 'on_disk> = ("
75 );
78 );
76
79
77 impl<'on_disk> DirstateMap<'on_disk> {
80 impl<'on_disk> DirstateMap<'on_disk> {
78 pub fn new(
81 #[timed]
82 pub fn new_v2(
83 on_disk: &'on_disk [u8],
84 ) -> Result<(Self, Option<DirstateParents>), DirstateError> {
85 if let Some(rest) = on_disk.drop_prefix(V2_FORMAT_MARKER) {
86 Self::new_v1(rest)
87 } else if on_disk.is_empty() {
88 Self::new_v1(on_disk)
89 } else {
90 return Err(HgError::corrupted(
91 "missing dirstate-v2 magic number",
92 )
93 .into());
94 }
95 }
96
97 #[timed]
98 pub fn new_v1(
79 on_disk: &'on_disk [u8],
99 on_disk: &'on_disk [u8],
80 ) -> Result<(Self, Option<DirstateParents>), DirstateError> {
100 ) -> Result<(Self, Option<DirstateParents>), DirstateError> {
81 let mut map = Self {
101 let mut map = Self {
@@ -84,23 +104,16 b" impl<'on_disk> DirstateMap<'on_disk> {"
84 nodes_with_entry_count: 0,
104 nodes_with_entry_count: 0,
85 nodes_with_copy_source_count: 0,
105 nodes_with_copy_source_count: 0,
86 };
106 };
87 let parents = map.read()?;
107 if map.on_disk.is_empty() {
88 Ok((map, parents))
108 return Ok((map, None));
89 }
90
91 /// Should only be called in `new`
92 #[timed]
93 fn read(&mut self) -> Result<Option<DirstateParents>, DirstateError> {
94 if self.on_disk.is_empty() {
95 return Ok(None);
96 }
109 }
97
110
98 let parents = parse_dirstate_entries(
111 let parents = parse_dirstate_entries(
99 self.on_disk,
112 map.on_disk,
100 |path, entry, copy_source| {
113 |path, entry, copy_source| {
101 let tracked = entry.state.is_tracked();
114 let tracked = entry.state.is_tracked();
102 let node = Self::get_or_insert_node(
115 let node = Self::get_or_insert_node(
103 &mut self.root,
116 &mut map.root,
104 path,
117 path,
105 WithBasename::to_cow_borrowed,
118 WithBasename::to_cow_borrowed,
106 |ancestor| {
119 |ancestor| {
@@ -119,14 +132,15 b" impl<'on_disk> DirstateMap<'on_disk> {"
119 );
132 );
120 node.entry = Some(*entry);
133 node.entry = Some(*entry);
121 node.copy_source = copy_source.map(Cow::Borrowed);
134 node.copy_source = copy_source.map(Cow::Borrowed);
122 self.nodes_with_entry_count += 1;
135 map.nodes_with_entry_count += 1;
123 if copy_source.is_some() {
136 if copy_source.is_some() {
124 self.nodes_with_copy_source_count += 1
137 map.nodes_with_copy_source_count += 1
125 }
138 }
126 },
139 },
127 )?;
140 )?;
141 let parents = Some(parents.clone());
128
142
129 Ok(Some(parents.clone()))
143 Ok((map, parents))
130 }
144 }
131
145
132 fn get_node(&self, path: &HgPath) -> Option<&Node> {
146 fn get_node(&self, path: &HgPath) -> Option<&Node> {
@@ -498,7 +512,8 b" impl<'on_disk> super::dispatch::Dirstate"
498 }
512 }
499 }
513 }
500
514
501 fn pack(
515 #[timed]
516 fn pack_v1(
502 &mut self,
517 &mut self,
503 parents: DirstateParents,
518 parents: DirstateParents,
504 now: Timestamp,
519 now: Timestamp,
@@ -533,6 +548,18 b" impl<'on_disk> super::dispatch::Dirstate"
533 Ok(packed)
548 Ok(packed)
534 }
549 }
535
550
551 #[timed]
552 fn pack_v2(
553 &mut self,
554 parents: DirstateParents,
555 now: Timestamp,
556 ) -> Result<Vec<u8>, DirstateError> {
557 // Inefficient but temporary
558 let mut v2 = V2_FORMAT_MARKER.to_vec();
559 v2.append(&mut self.pack_v1(parents, now)?);
560 Ok(v2)
561 }
562
536 fn set_all_dirs(&mut self) -> Result<(), DirstateMapError> {
563 fn set_all_dirs(&mut self) -> Result<(), DirstateMapError> {
537 // Do nothing, this `DirstateMap` does not a separate `all_dirs` that
564 // Do nothing, this `DirstateMap` does not a separate `all_dirs` that
538 // needs to be recomputed
565 // needs to be recomputed
@@ -73,7 +73,13 b' pub trait DirstateMapMethods {'
73 directory: &HgPath,
73 directory: &HgPath,
74 ) -> Result<bool, DirstateMapError>;
74 ) -> Result<bool, DirstateMapError>;
75
75
76 fn pack(
76 fn pack_v1(
77 &mut self,
78 parents: DirstateParents,
79 now: Timestamp,
80 ) -> Result<Vec<u8>, DirstateError>;
81
82 fn pack_v2(
77 &mut self,
83 &mut self,
78 parents: DirstateParents,
84 parents: DirstateParents,
79 now: Timestamp,
85 now: Timestamp,
@@ -211,7 +217,7 b' impl DirstateMapMethods for DirstateMap '
211 self.has_dir(directory)
217 self.has_dir(directory)
212 }
218 }
213
219
214 fn pack(
220 fn pack_v1(
215 &mut self,
221 &mut self,
216 parents: DirstateParents,
222 parents: DirstateParents,
217 now: Timestamp,
223 now: Timestamp,
@@ -219,6 +225,16 b' impl DirstateMapMethods for DirstateMap '
219 self.pack(parents, now)
225 self.pack(parents, now)
220 }
226 }
221
227
228 fn pack_v2(
229 &mut self,
230 _parents: DirstateParents,
231 _now: Timestamp,
232 ) -> Result<Vec<u8>, DirstateError> {
233 panic!(
234 "should have used dirstate_tree::DirstateMap to use the v2 format"
235 )
236 }
237
222 fn set_all_dirs(&mut self) -> Result<(), DirstateMapError> {
238 fn set_all_dirs(&mut self) -> Result<(), DirstateMapError> {
223 self.set_all_dirs()
239 self.set_all_dirs()
224 }
240 }
@@ -26,6 +26,7 b' use cpython::{'
26 exc, PyBytes, PyDict, PyErr, PyList, PyModule, PyObject, PyResult,
26 exc, PyBytes, PyDict, PyErr, PyList, PyModule, PyObject, PyResult,
27 PySequence, Python,
27 PySequence, Python,
28 };
28 };
29 use hg::dirstate_tree::on_disk::V2_FORMAT_MARKER;
29 use hg::{utils::hg_path::HgPathBuf, DirstateEntry, EntryState, StateMap};
30 use hg::{utils::hg_path::HgPathBuf, DirstateEntry, EntryState, StateMap};
30 use libc::{c_char, c_int};
31 use libc::{c_char, c_int};
31 use std::convert::TryFrom;
32 use std::convert::TryFrom;
@@ -117,6 +118,7 b' pub fn init_module(py: Python, package: '
117 )?;
118 )?;
118 m.add_class::<Dirs>(py)?;
119 m.add_class::<Dirs>(py)?;
119 m.add_class::<DirstateMap>(py)?;
120 m.add_class::<DirstateMap>(py)?;
121 m.add(py, "V2_FORMAT_MARKER", PyBytes::new(py, V2_FORMAT_MARKER))?;
120 m.add(
122 m.add(
121 py,
123 py,
122 "status",
124 "status",
@@ -55,13 +55,17 b' py_class!(pub class DirstateMap |py| {'
55
55
56 /// Returns a `(dirstate_map, parents)` tuple
56 /// Returns a `(dirstate_map, parents)` tuple
57 @staticmethod
57 @staticmethod
58 def new(use_dirstate_tree: bool, on_disk: PyBytes) -> PyResult<PyObject> {
58 def new(
59 let dirstate_error = |_: DirstateError| {
59 use_dirstate_tree: bool,
60 PyErr::new::<exc::OSError, _>(py, "Dirstate error".to_string())
60 use_dirstate_v2: bool,
61 on_disk: PyBytes,
62 ) -> PyResult<PyObject> {
63 let dirstate_error = |e: DirstateError| {
64 PyErr::new::<exc::OSError, _>(py, format!("Dirstate error: {:?}", e))
61 };
65 };
62 let (inner, parents) = if use_dirstate_tree {
66 let (inner, parents) = if use_dirstate_tree || use_dirstate_v2 {
63 let (map, parents) =
67 let (map, parents) =
64 OwningDirstateMap::new(py, on_disk)
68 OwningDirstateMap::new(py, on_disk, use_dirstate_v2)
65 .map_err(dirstate_error)?;
69 .map_err(dirstate_error)?;
66 (Box::new(map) as _, parents)
70 (Box::new(map) as _, parents)
67 } else {
71 } else {
@@ -288,6 +292,7 b' py_class!(pub class DirstateMap |py| {'
288
292
289 def write(
293 def write(
290 &self,
294 &self,
295 use_dirstate_v2: bool,
291 p1: PyObject,
296 p1: PyObject,
292 p2: PyObject,
297 p2: PyObject,
293 now: PyObject
298 now: PyObject
@@ -298,7 +303,13 b' py_class!(pub class DirstateMap |py| {'
298 p2: extract_node_id(py, &p2)?,
303 p2: extract_node_id(py, &p2)?,
299 };
304 };
300
305
301 match self.inner(py).borrow_mut().pack(parents, now) {
306 let mut inner = self.inner(py).borrow_mut();
307 let result = if use_dirstate_v2 {
308 inner.pack_v2(parents, now)
309 } else {
310 inner.pack_v1(parents, now)
311 };
312 match result {
302 Ok(packed) => Ok(PyBytes::new(py, &packed)),
313 Ok(packed) => Ok(PyBytes::new(py, &packed)),
303 Err(_) => Err(PyErr::new::<exc::OSError, _>(
314 Err(_) => Err(PyErr::new::<exc::OSError, _>(
304 py,
315 py,
@@ -101,12 +101,20 b' impl DirstateMapMethods for OwningDirsta'
101 self.get_mut().has_dir(directory)
101 self.get_mut().has_dir(directory)
102 }
102 }
103
103
104 fn pack(
104 fn pack_v1(
105 &mut self,
105 &mut self,
106 parents: DirstateParents,
106 parents: DirstateParents,
107 now: Timestamp,
107 now: Timestamp,
108 ) -> Result<Vec<u8>, DirstateError> {
108 ) -> Result<Vec<u8>, DirstateError> {
109 self.get_mut().pack(parents, now)
109 self.get_mut().pack_v1(parents, now)
110 }
111
112 fn pack_v2(
113 &mut self,
114 parents: DirstateParents,
115 now: Timestamp,
116 ) -> Result<Vec<u8>, DirstateError> {
117 self.get_mut().pack_v2(parents, now)
110 }
118 }
111
119
112 fn set_all_dirs(&mut self) -> Result<(), DirstateMapError> {
120 fn set_all_dirs(&mut self) -> Result<(), DirstateMapError> {
@@ -31,9 +31,14 b' impl OwningDirstateMap {'
31 pub fn new(
31 pub fn new(
32 py: Python,
32 py: Python,
33 on_disk: PyBytes,
33 on_disk: PyBytes,
34 use_dirstate_v2: bool,
34 ) -> Result<(Self, Option<DirstateParents>), DirstateError> {
35 ) -> Result<(Self, Option<DirstateParents>), DirstateError> {
35 let bytes: &'_ [u8] = on_disk.data(py);
36 let bytes: &'_ [u8] = on_disk.data(py);
36 let (map, parents) = DirstateMap::new(bytes)?;
37 let (map, parents) = if use_dirstate_v2 {
38 DirstateMap::new_v2(bytes)?
39 } else {
40 DirstateMap::new_v1(bytes)?
41 };
37
42
38 // Like in `bytes` above, this `'_` lifetime parameter borrows from
43 // Like in `bytes` above, this `'_` lifetime parameter borrows from
39 // the bytes buffer owned by `on_disk`.
44 // the bytes buffer owned by `on_disk`.
General Comments 0
You need to be logged in to leave comments. Login now