##// 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 25 httpconnection,
26 26 match as matchmod,
27 27 pycompat,
28 requirements,
28 29 scmutil,
29 30 sparse,
30 31 util,
@@ -197,6 +198,7 b' def openlfdirstate(ui, repo, create=True'
197 198 vfs = repo.vfs
198 199 lfstoredir = longname
199 200 opener = vfsmod.vfs(vfs.join(lfstoredir))
201 use_dirstate_v2 = requirements.DIRSTATE_V2_REQUIREMENT in repo.requirements
200 202 lfdirstate = largefilesdirstate(
201 203 opener,
202 204 ui,
@@ -204,6 +206,7 b' def openlfdirstate(ui, repo, create=True'
204 206 repo.dirstate._validate,
205 207 lambda: sparse.matcher(repo),
206 208 repo.nodeconstants,
209 use_dirstate_v2,
207 210 )
208 211
209 212 # If the largefiles dirstate does not exist, populate and create
@@ -75,7 +75,14 b' def _getfsnow(vfs):'
75 75 @interfaceutil.implementer(intdirstate.idirstate)
76 76 class dirstate(object):
77 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 87 """Create a new dirstate object.
81 88
@@ -83,6 +90,7 b' class dirstate(object):'
83 90 dirstate file; root is the root of the directory tracked by
84 91 the dirstate.
85 92 """
93 self._use_dirstate_v2 = use_dirstate_v2
86 94 self._nodeconstants = nodeconstants
87 95 self._opener = opener
88 96 self._validate = validate
@@ -141,7 +149,11 b' class dirstate(object):'
141 149 def _map(self):
142 150 """Return the dirstate contents (see documentation for dirstatemap)."""
143 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 158 return self._map
147 159
@@ -1435,13 +1447,16 b' class dirstatemap(object):'
1435 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 1451 self._ui = ui
1440 1452 self._opener = opener
1441 1453 self._root = root
1442 1454 self._filename = b'dirstate'
1443 1455 self._nodelen = 20
1444 1456 self._nodeconstants = nodeconstants
1457 assert (
1458 not use_dirstate_v2
1459 ), "should have detected unsupported requirement"
1445 1460
1446 1461 self._parents = None
1447 1462 self._dirtyparents = False
@@ -1746,13 +1761,14 b' class dirstatemap(object):'
1746 1761 if rustmod is not None:
1747 1762
1748 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 1766 self._nodeconstants = nodeconstants
1751 1767 self._ui = ui
1752 1768 self._opener = opener
1753 1769 self._root = root
1754 1770 self._filename = b'dirstate'
1755 self._nodelen = 20
1771 self._nodelen = 20 # Also update Rust code when changing this!
1756 1772 self._parents = None
1757 1773 self._dirtyparents = False
1758 1774
@@ -1832,9 +1848,14 b' if rustmod is not None:'
1832 1848
1833 1849 def parents(self):
1834 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 1856 try:
1836 1857 fp = self._opendirstatefile()
1837 st = fp.read(40)
1858 st = fp.read(read_len)
1838 1859 fp.close()
1839 1860 except IOError as err:
1840 1861 if err.errno != errno.ENOENT:
@@ -1843,7 +1864,8 b' if rustmod is not None:'
1843 1864 st = b''
1844 1865
1845 1866 l = len(st)
1846 if l == self._nodelen * 2:
1867 if l == read_len:
1868 st = st[offset:]
1847 1869 self._parents = (
1848 1870 st[: self._nodelen],
1849 1871 st[self._nodelen : 2 * self._nodelen],
@@ -1887,7 +1909,7 b' if rustmod is not None:'
1887 1909 False,
1888 1910 )
1889 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 1915 if parents and not self._dirtyparents:
@@ -1900,7 +1922,10 b' if rustmod is not None:'
1900 1922
1901 1923 def write(self, st, now):
1902 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 1929 st.close()
1905 1930 self._dirtyparents = False
1906 1931
@@ -6,7 +6,15 b' from . import util as interfaceutil'
6 6
7 7
8 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 18 """Create a new dirstate object.
11 19
12 20 opener is an open()-like callable that can be used to open the
@@ -1690,6 +1690,8 b' class localrepository(object):'
1690 1690 def _makedirstate(self):
1691 1691 """Extension point for wrapping the dirstate per-repo."""
1692 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 1696 return dirstate.dirstate(
1695 1697 self.vfs,
@@ -1698,6 +1700,7 b' class localrepository(object):'
1698 1700 self._dirstatevalidate,
1699 1701 sparsematchfn,
1700 1702 self.nodeconstants,
1703 use_dirstate_v2,
1701 1704 )
1702 1705
1703 1706 def _dirstatevalidate(self, node):
@@ -1,4 +1,5 b''
1 1 pub mod dirstate_map;
2 2 pub mod dispatch;
3 pub mod on_disk;
3 4 pub mod path_with_basename;
4 5 mod status;
@@ -4,14 +4,17 b' use std::borrow::Cow;'
4 4 use std::convert::TryInto;
5 5 use std::path::PathBuf;
6 6
7 use super::on_disk::V2_FORMAT_MARKER;
7 8 use super::path_with_basename::WithBasename;
8 9 use crate::dirstate::parsers::clear_ambiguous_mtime;
9 10 use crate::dirstate::parsers::pack_entry;
10 11 use crate::dirstate::parsers::packed_entry_size;
11 12 use crate::dirstate::parsers::parse_dirstate_entries;
12 13 use crate::dirstate::parsers::Timestamp;
14 use crate::errors::HgError;
13 15 use crate::matchers::Matcher;
14 16 use crate::utils::hg_path::{HgPath, HgPathBuf};
17 use crate::utils::SliceExt;
15 18 use crate::CopyMapIter;
16 19 use crate::DirstateEntry;
17 20 use crate::DirstateError;
@@ -75,7 +78,24 b" type NodeDataMut<'tree, 'on_disk> = ("
75 78 );
76 79
77 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 99 on_disk: &'on_disk [u8],
80 100 ) -> Result<(Self, Option<DirstateParents>), DirstateError> {
81 101 let mut map = Self {
@@ -84,23 +104,16 b" impl<'on_disk> DirstateMap<'on_disk> {"
84 104 nodes_with_entry_count: 0,
85 105 nodes_with_copy_source_count: 0,
86 106 };
87 let parents = map.read()?;
88 Ok((map, parents))
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);
107 if map.on_disk.is_empty() {
108 return Ok((map, None));
96 109 }
97 110
98 111 let parents = parse_dirstate_entries(
99 self.on_disk,
112 map.on_disk,
100 113 |path, entry, copy_source| {
101 114 let tracked = entry.state.is_tracked();
102 115 let node = Self::get_or_insert_node(
103 &mut self.root,
116 &mut map.root,
104 117 path,
105 118 WithBasename::to_cow_borrowed,
106 119 |ancestor| {
@@ -119,14 +132,15 b" impl<'on_disk> DirstateMap<'on_disk> {"
119 132 );
120 133 node.entry = Some(*entry);
121 134 node.copy_source = copy_source.map(Cow::Borrowed);
122 self.nodes_with_entry_count += 1;
135 map.nodes_with_entry_count += 1;
123 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 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 517 &mut self,
503 518 parents: DirstateParents,
504 519 now: Timestamp,
@@ -533,6 +548,18 b" impl<'on_disk> super::dispatch::Dirstate"
533 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 563 fn set_all_dirs(&mut self) -> Result<(), DirstateMapError> {
537 564 // Do nothing, this `DirstateMap` does not a separate `all_dirs` that
538 565 // needs to be recomputed
@@ -73,7 +73,13 b' pub trait DirstateMapMethods {'
73 73 directory: &HgPath,
74 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 83 &mut self,
78 84 parents: DirstateParents,
79 85 now: Timestamp,
@@ -211,7 +217,7 b' impl DirstateMapMethods for DirstateMap '
211 217 self.has_dir(directory)
212 218 }
213 219
214 fn pack(
220 fn pack_v1(
215 221 &mut self,
216 222 parents: DirstateParents,
217 223 now: Timestamp,
@@ -219,6 +225,16 b' impl DirstateMapMethods for DirstateMap '
219 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 238 fn set_all_dirs(&mut self) -> Result<(), DirstateMapError> {
223 239 self.set_all_dirs()
224 240 }
@@ -26,6 +26,7 b' use cpython::{'
26 26 exc, PyBytes, PyDict, PyErr, PyList, PyModule, PyObject, PyResult,
27 27 PySequence, Python,
28 28 };
29 use hg::dirstate_tree::on_disk::V2_FORMAT_MARKER;
29 30 use hg::{utils::hg_path::HgPathBuf, DirstateEntry, EntryState, StateMap};
30 31 use libc::{c_char, c_int};
31 32 use std::convert::TryFrom;
@@ -117,6 +118,7 b' pub fn init_module(py: Python, package: '
117 118 )?;
118 119 m.add_class::<Dirs>(py)?;
119 120 m.add_class::<DirstateMap>(py)?;
121 m.add(py, "V2_FORMAT_MARKER", PyBytes::new(py, V2_FORMAT_MARKER))?;
120 122 m.add(
121 123 py,
122 124 "status",
@@ -55,13 +55,17 b' py_class!(pub class DirstateMap |py| {'
55 55
56 56 /// Returns a `(dirstate_map, parents)` tuple
57 57 @staticmethod
58 def new(use_dirstate_tree: bool, on_disk: PyBytes) -> PyResult<PyObject> {
59 let dirstate_error = |_: DirstateError| {
60 PyErr::new::<exc::OSError, _>(py, "Dirstate error".to_string())
58 def new(
59 use_dirstate_tree: bool,
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 67 let (map, parents) =
64 OwningDirstateMap::new(py, on_disk)
68 OwningDirstateMap::new(py, on_disk, use_dirstate_v2)
65 69 .map_err(dirstate_error)?;
66 70 (Box::new(map) as _, parents)
67 71 } else {
@@ -288,6 +292,7 b' py_class!(pub class DirstateMap |py| {'
288 292
289 293 def write(
290 294 &self,
295 use_dirstate_v2: bool,
291 296 p1: PyObject,
292 297 p2: PyObject,
293 298 now: PyObject
@@ -298,7 +303,13 b' py_class!(pub class DirstateMap |py| {'
298 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 313 Ok(packed) => Ok(PyBytes::new(py, &packed)),
303 314 Err(_) => Err(PyErr::new::<exc::OSError, _>(
304 315 py,
@@ -101,12 +101,20 b' impl DirstateMapMethods for OwningDirsta'
101 101 self.get_mut().has_dir(directory)
102 102 }
103 103
104 fn pack(
104 fn pack_v1(
105 105 &mut self,
106 106 parents: DirstateParents,
107 107 now: Timestamp,
108 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 120 fn set_all_dirs(&mut self) -> Result<(), DirstateMapError> {
@@ -31,9 +31,14 b' impl OwningDirstateMap {'
31 31 pub fn new(
32 32 py: Python,
33 33 on_disk: PyBytes,
34 use_dirstate_v2: bool,
34 35 ) -> Result<(Self, Option<DirstateParents>), DirstateError> {
35 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 43 // Like in `bytes` above, this `'_` lifetime parameter borrows from
39 44 // the bytes buffer owned by `on_disk`.
General Comments 0
You need to be logged in to leave comments. Login now