##// END OF EJS Templates
dirstate-v2: Separate iterators for dirfoldmap and debugdirstate...
Simon Sapin -
r48483:284a2026 default
parent child Browse files
Show More
@@ -942,7 +942,12 b' def debugdeltachain(ui, repo, file_=None'
942 ),
942 ),
943 (b'', b'dates', True, _(b'display the saved mtime')),
943 (b'', b'dates', True, _(b'display the saved mtime')),
944 (b'', b'datesort', None, _(b'sort by saved mtime')),
944 (b'', b'datesort', None, _(b'sort by saved mtime')),
945 (b'', b'dirs', False, _(b'display directories')),
945 (
946 b'',
947 b'all',
948 False,
949 _(b'display dirstate-v2 tree nodes that would not exist in v1'),
950 ),
946 ],
951 ],
947 _(b'[OPTION]...'),
952 _(b'[OPTION]...'),
948 )
953 )
@@ -961,9 +966,10 b' def debugstate(ui, repo, **opts):'
961 ) # sort by mtime, then by filename
966 ) # sort by mtime, then by filename
962 else:
967 else:
963 keyfunc = None # sort by filename
968 keyfunc = None # sort by filename
964 entries = list(pycompat.iteritems(repo.dirstate))
969 if opts['all']:
965 if opts['dirs']:
970 entries = list(repo.dirstate._map.debug_iter())
966 entries.extend(repo.dirstate.directories())
971 else:
972 entries = list(pycompat.iteritems(repo.dirstate))
967 entries.sort(key=keyfunc)
973 entries.sort(key=keyfunc)
968 for file_, ent in entries:
974 for file_, ent in entries:
969 if ent.v1_mtime() == -1:
975 if ent.v1_mtime() == -1:
@@ -105,10 +105,6 b' class dirstatemap(object):'
105 self._map
105 self._map
106 return self.copymap
106 return self.copymap
107
107
108 def directories(self):
109 # Rust / dirstate-v2 only
110 return []
111
112 def clear(self):
108 def clear(self):
113 self._map.clear()
109 self._map.clear()
114 self.copymap.clear()
110 self.copymap.clear()
@@ -126,6 +122,8 b' class dirstatemap(object):'
126 # forward for python2,3 compat
122 # forward for python2,3 compat
127 iteritems = items
123 iteritems = items
128
124
125 debug_iter = items
126
129 def __len__(self):
127 def __len__(self):
130 return len(self._map)
128 return len(self._map)
131
129
@@ -525,6 +523,9 b' if rustmod is not None:'
525 def directories(self):
523 def directories(self):
526 return self._rustmap.directories()
524 return self._rustmap.directories()
527
525
526 def debug_iter(self):
527 return self._rustmap.debug_iter()
528
528 def preload(self):
529 def preload(self):
529 self._rustmap
530 self._rustmap
530
531
@@ -746,6 +747,6 b' if rustmod is not None:'
746 def dirfoldmap(self):
747 def dirfoldmap(self):
747 f = {}
748 f = {}
748 normcase = util.normcase
749 normcase = util.normcase
749 for name, _pseudo_entry in self.directories():
750 for name in self._rustmap.tracked_dirs():
750 f[normcase(name)] = name
751 f[normcase(name)] = name
751 return f
752 return f
@@ -65,6 +65,12 b' impl DirstateEntry {'
65 let fs_exec_bit = filesystem_metadata.mode() & EXEC_BIT_MASK;
65 let fs_exec_bit = filesystem_metadata.mode() & EXEC_BIT_MASK;
66 dirstate_exec_bit != fs_exec_bit
66 dirstate_exec_bit != fs_exec_bit
67 }
67 }
68
69 /// Returns a `(state, mode, size, mtime)` tuple as for
70 /// `DirstateMapMethods::debug_iter`.
71 pub fn debug_tuple(&self) -> (u8, i32, i32, i32) {
72 (self.state.into(), self.mode, self.size, self.mtime)
73 }
68 }
74 }
69
75
70 #[derive(BytesCast)]
76 #[derive(BytesCast)]
@@ -1246,27 +1246,50 b" impl<'on_disk> super::dispatch::Dirstate"
1246 }))
1246 }))
1247 }
1247 }
1248
1248
1249 fn iter_directories(
1249 fn iter_tracked_dirs(
1250 &mut self,
1251 ) -> Result<
1252 Box<
1253 dyn Iterator<Item = Result<&HgPath, DirstateV2ParseError>>
1254 + Send
1255 + '_,
1256 >,
1257 DirstateError,
1258 > {
1259 let on_disk = self.on_disk;
1260 Ok(Box::new(filter_map_results(
1261 self.iter_nodes(),
1262 move |node| {
1263 Ok(if node.tracked_descendants_count() > 0 {
1264 Some(node.full_path(on_disk)?)
1265 } else {
1266 None
1267 })
1268 },
1269 )))
1270 }
1271
1272 fn debug_iter(
1250 &self,
1273 &self,
1251 ) -> Box<
1274 ) -> Box<
1252 dyn Iterator<
1275 dyn Iterator<
1253 Item = Result<
1276 Item = Result<
1254 (&HgPath, Option<Timestamp>),
1277 (&HgPath, (u8, i32, i32, i32)),
1255 DirstateV2ParseError,
1278 DirstateV2ParseError,
1256 >,
1279 >,
1257 > + Send
1280 > + Send
1258 + '_,
1281 + '_,
1259 > {
1282 > {
1260 Box::new(filter_map_results(self.iter_nodes(), move |node| {
1283 Box::new(self.iter_nodes().map(move |node| {
1261 Ok(if node.state()?.is_none() {
1284 let node = node?;
1262 Some((
1285 let debug_tuple = if let Some(entry) = node.entry()? {
1263 node.full_path(self.on_disk)?,
1286 entry.debug_tuple()
1264 node.cached_directory_mtime()
1287 } else if let Some(mtime) = node.cached_directory_mtime() {
1265 .map(|mtime| Timestamp(mtime.seconds())),
1288 (b' ', 0, -1, mtime.seconds() as i32)
1266 ))
1267 } else {
1289 } else {
1268 None
1290 (b' ', 0, -1, -1)
1269 })
1291 };
1292 Ok((node.full_path(self.on_disk)?, debug_tuple))
1270 }))
1293 }))
1271 }
1294 }
1272 }
1295 }
@@ -259,20 +259,40 b' pub trait DirstateMapMethods {'
259 /// are `Result`s.
259 /// are `Result`s.
260 fn iter(&self) -> StateMapIter<'_>;
260 fn iter(&self) -> StateMapIter<'_>;
261
261
262 /// In the tree dirstate, return an iterator of "directory" (entry-less)
262 /// Returns an iterator of tracked directories.
263 /// nodes with the data stored for them. This is for `hg debugdirstate
264 /// --dirs`.
265 ///
263 ///
266 /// In the flat dirstate, returns an empty iterator.
264 /// This is the paths for which `has_tracked_dir` would return true.
265 /// Or, in other words, the union of ancestor paths of all paths that have
266 /// an associated entry in a "tracked" state in this dirstate map.
267 ///
267 ///
268 /// Because parse errors can happen during iteration, the iterated items
268 /// Because parse errors can happen during iteration, the iterated items
269 /// are `Result`s.
269 /// are `Result`s.
270 fn iter_directories(
270 fn iter_tracked_dirs(
271 &mut self,
272 ) -> Result<
273 Box<
274 dyn Iterator<Item = Result<&HgPath, DirstateV2ParseError>>
275 + Send
276 + '_,
277 >,
278 DirstateError,
279 >;
280
281 /// Return an iterator of `(path, (state, mode, size, mtime))` for every
282 /// node stored in this dirstate map, for the purpose of the `hg
283 /// debugdirstate` command.
284 ///
285 /// For nodes that don’t have an entry, `state` is the ASCII space.
286 /// An `mtime` may still be present. It is used to optimize `status`.
287 ///
288 /// Because parse errors can happen during iteration, the iterated items
289 /// are `Result`s.
290 fn debug_iter(
271 &self,
291 &self,
272 ) -> Box<
292 ) -> Box<
273 dyn Iterator<
293 dyn Iterator<
274 Item = Result<
294 Item = Result<
275 (&HgPath, Option<Timestamp>),
295 (&HgPath, (u8, i32, i32, i32)),
276 DirstateV2ParseError,
296 DirstateV2ParseError,
277 >,
297 >,
278 > + Send
298 > + Send
@@ -476,17 +496,41 b' impl DirstateMapMethods for DirstateMap '
476 Box::new((&**self).iter().map(|(key, value)| Ok((&**key, *value))))
496 Box::new((&**self).iter().map(|(key, value)| Ok((&**key, *value))))
477 }
497 }
478
498
479 fn iter_directories(
499 fn iter_tracked_dirs(
500 &mut self,
501 ) -> Result<
502 Box<
503 dyn Iterator<Item = Result<&HgPath, DirstateV2ParseError>>
504 + Send
505 + '_,
506 >,
507 DirstateError,
508 > {
509 self.set_all_dirs()?;
510 Ok(Box::new(
511 self.all_dirs
512 .as_ref()
513 .unwrap()
514 .iter()
515 .map(|path| Ok(&**path)),
516 ))
517 }
518
519 fn debug_iter(
480 &self,
520 &self,
481 ) -> Box<
521 ) -> Box<
482 dyn Iterator<
522 dyn Iterator<
483 Item = Result<
523 Item = Result<
484 (&HgPath, Option<Timestamp>),
524 (&HgPath, (u8, i32, i32, i32)),
485 DirstateV2ParseError,
525 DirstateV2ParseError,
486 >,
526 >,
487 > + Send
527 > + Send
488 + '_,
528 + '_,
489 > {
529 > {
490 Box::new(std::iter::empty())
530 Box::new(
531 (&**self)
532 .iter()
533 .map(|(path, entry)| Ok((&**path, entry.debug_tuple()))),
534 )
491 }
535 }
492 }
536 }
@@ -52,9 +52,6 b' pub fn make_dirstate_item('
52 py: Python,
52 py: Python,
53 entry: &DirstateEntry,
53 entry: &DirstateEntry,
54 ) -> PyResult<PyObject> {
54 ) -> PyResult<PyObject> {
55 // might be silly to retrieve capsule function in hot loop
56 let make = make_dirstate_item_capi::retrieve(py)?;
57
58 let &DirstateEntry {
55 let &DirstateEntry {
59 state,
56 state,
60 mode,
57 mode,
@@ -65,22 +62,19 b' pub fn make_dirstate_item('
65 // because Into<u8> has a specific implementation while `as c_char` would
62 // because Into<u8> has a specific implementation while `as c_char` would
66 // just do a naive enum cast.
63 // just do a naive enum cast.
67 let state_code: u8 = state.into();
64 let state_code: u8 = state.into();
68
65 make_dirstate_item_raw(py, state_code, mode, size, mtime)
69 let maybe_obj = unsafe {
70 let ptr = make(state_code as c_char, mode, size, mtime);
71 PyObject::from_owned_ptr_opt(py, ptr)
72 };
73 maybe_obj.ok_or_else(|| PyErr::fetch(py))
74 }
66 }
75
67
76 // XXX a bit strange to have a dedicated function, but directory are not
68 pub fn make_dirstate_item_raw(
77 // treated as dirstate node by hg-core for now so…
69 py: Python,
78 pub fn make_directory_item(py: Python, mtime: i32) -> PyResult<PyObject> {
70 state: u8,
79 // might be silly to retrieve capsule function in hot loop
71 mode: i32,
72 size: i32,
73 mtime: i32,
74 ) -> PyResult<PyObject> {
80 let make = make_dirstate_item_capi::retrieve(py)?;
75 let make = make_dirstate_item_capi::retrieve(py)?;
81
82 let maybe_obj = unsafe {
76 let maybe_obj = unsafe {
83 let ptr = make(b'd' as c_char, 0 as i32, 0 as i32, mtime);
77 let ptr = make(state as c_char, mode, size, mtime);
84 PyObject::from_owned_ptr_opt(py, ptr)
78 PyObject::from_owned_ptr_opt(py, ptr)
85 };
79 };
86 maybe_obj.ok_or_else(|| PyErr::fetch(py))
80 maybe_obj.ok_or_else(|| PyErr::fetch(py))
@@ -19,8 +19,8 b' use cpython::{'
19
19
20 use crate::{
20 use crate::{
21 dirstate::copymap::{CopyMap, CopyMapItemsIterator, CopyMapKeysIterator},
21 dirstate::copymap::{CopyMap, CopyMapItemsIterator, CopyMapKeysIterator},
22 dirstate::make_directory_item,
23 dirstate::make_dirstate_item,
22 dirstate::make_dirstate_item,
23 dirstate::make_dirstate_item_raw,
24 dirstate::non_normal_entries::{
24 dirstate::non_normal_entries::{
25 NonNormalEntries, NonNormalEntriesIterator,
25 NonNormalEntries, NonNormalEntriesIterator,
26 },
26 },
@@ -61,17 +61,14 b' py_class!(pub class DirstateMap |py| {'
61 use_dirstate_tree: bool,
61 use_dirstate_tree: bool,
62 on_disk: PyBytes,
62 on_disk: PyBytes,
63 ) -> PyResult<PyObject> {
63 ) -> PyResult<PyObject> {
64 let dirstate_error = |e: DirstateError| {
65 PyErr::new::<exc::OSError, _>(py, format!("Dirstate error: {:?}", e))
66 };
67 let (inner, parents) = if use_dirstate_tree {
64 let (inner, parents) = if use_dirstate_tree {
68 let (map, parents) = OwningDirstateMap::new_v1(py, on_disk)
65 let (map, parents) = OwningDirstateMap::new_v1(py, on_disk)
69 .map_err(dirstate_error)?;
66 .map_err(|e| dirstate_error(py, e))?;
70 (Box::new(map) as _, parents)
67 (Box::new(map) as _, parents)
71 } else {
68 } else {
72 let bytes = on_disk.data(py);
69 let bytes = on_disk.data(py);
73 let mut map = RustDirstateMap::default();
70 let mut map = RustDirstateMap::default();
74 let parents = map.read(bytes).map_err(dirstate_error)?;
71 let parents = map.read(bytes).map_err(|e| dirstate_error(py, e))?;
75 (Box::new(map) as _, parents)
72 (Box::new(map) as _, parents)
76 };
73 };
77 let map = Self::create_instance(py, inner)?;
74 let map = Self::create_instance(py, inner)?;
@@ -550,19 +547,29 b' py_class!(pub class DirstateMap |py| {'
550 )
547 )
551 }
548 }
552
549
553 def directories(&self) -> PyResult<PyList> {
550 def tracked_dirs(&self) -> PyResult<PyList> {
554 let dirs = PyList::new(py, &[]);
551 let dirs = PyList::new(py, &[]);
555 for item in self.inner(py).borrow().iter_directories() {
552 for path in self.inner(py).borrow_mut().iter_tracked_dirs()
556 let (path, mtime) = item.map_err(|e| v2_error(py, e))?;
553 .map_err(|e |dirstate_error(py, e))?
554 {
555 let path = path.map_err(|e| v2_error(py, e))?;
557 let path = PyBytes::new(py, path.as_bytes());
556 let path = PyBytes::new(py, path.as_bytes());
558 let mtime = mtime.map(|t| t.0).unwrap_or(-1);
557 dirs.append(py, path.into_object())
559 let item = make_directory_item(py, mtime as i32)?;
560 let tuple = (path, item);
561 dirs.append(py, tuple.to_py_object(py).into_object())
562 }
558 }
563 Ok(dirs)
559 Ok(dirs)
564 }
560 }
565
561
562 def debug_iter(&self) -> PyResult<PyList> {
563 let dirs = PyList::new(py, &[]);
564 for item in self.inner(py).borrow().debug_iter() {
565 let (path, (state, mode, size, mtime)) =
566 item.map_err(|e| v2_error(py, e))?;
567 let path = PyBytes::new(py, path.as_bytes());
568 let item = make_dirstate_item_raw(py, state, mode, size, mtime)?;
569 dirs.append(py, (path, item).to_py_object(py).into_object())
570 }
571 Ok(dirs)
572 }
566 });
573 });
567
574
568 impl DirstateMap {
575 impl DirstateMap {
@@ -616,3 +623,7 b' fn extract_node_id(py: Python, obj: &PyO'
616 pub(super) fn v2_error(py: Python<'_>, _: DirstateV2ParseError) -> PyErr {
623 pub(super) fn v2_error(py: Python<'_>, _: DirstateV2ParseError) -> PyErr {
617 PyErr::new::<exc::ValueError, _>(py, "corrupted dirstate-v2")
624 PyErr::new::<exc::ValueError, _>(py, "corrupted dirstate-v2")
618 }
625 }
626
627 fn dirstate_error(py: Python<'_>, e: DirstateError) -> PyErr {
628 PyErr::new::<exc::OSError, _>(py, format!("Dirstate error: {:?}", e))
629 }
@@ -203,17 +203,30 b' impl DirstateMapMethods for OwningDirsta'
203 self.get().iter()
203 self.get().iter()
204 }
204 }
205
205
206 fn iter_directories(
206 fn iter_tracked_dirs(
207 &mut self,
208 ) -> Result<
209 Box<
210 dyn Iterator<Item = Result<&HgPath, DirstateV2ParseError>>
211 + Send
212 + '_,
213 >,
214 DirstateError,
215 > {
216 self.get_mut().iter_tracked_dirs()
217 }
218
219 fn debug_iter(
207 &self,
220 &self,
208 ) -> Box<
221 ) -> Box<
209 dyn Iterator<
222 dyn Iterator<
210 Item = Result<
223 Item = Result<
211 (&HgPath, Option<Timestamp>),
224 (&HgPath, (u8, i32, i32, i32)),
212 DirstateV2ParseError,
225 DirstateV2ParseError,
213 >,
226 >,
214 > + Send
227 > + Send
215 + '_,
228 + '_,
216 > {
229 > {
217 self.get().iter_directories()
230 self.get().debug_iter()
218 }
231 }
219 }
232 }
@@ -284,7 +284,7 b' Show all commands + options'
284 debugdate: extended
284 debugdate: extended
285 debugdeltachain: changelog, manifest, dir, template
285 debugdeltachain: changelog, manifest, dir, template
286 debugdirstateignorepatternshash:
286 debugdirstateignorepatternshash:
287 debugdirstate: nodates, dates, datesort, dirs
287 debugdirstate: nodates, dates, datesort, all
288 debugdiscovery: old, nonheads, rev, seed, local-as-revs, remote-as-revs, ssh, remotecmd, insecure, template
288 debugdiscovery: old, nonheads, rev, seed, local-as-revs, remote-as-revs, ssh, remotecmd, insecure, template
289 debugdownload: output
289 debugdownload: output
290 debugextensions: template
290 debugextensions: template
@@ -929,23 +929,23 b' Check read_dir caching'
929
929
930 The cached mtime is initially unset
930 The cached mtime is initially unset
931
931
932 $ hg debugdirstate --dirs --no-dates | grep '^d'
932 $ hg debugdirstate --all --no-dates | grep '^ '
933 d 0 0 unset subdir
933 0 -1 unset subdir
934
934
935 It is still not set when there are unknown files
935 It is still not set when there are unknown files
936
936
937 $ touch subdir/unknown
937 $ touch subdir/unknown
938 $ hg status
938 $ hg status
939 ? subdir/unknown
939 ? subdir/unknown
940 $ hg debugdirstate --dirs --no-dates | grep '^d'
940 $ hg debugdirstate --all --no-dates | grep '^ '
941 d 0 0 unset subdir
941 0 -1 unset subdir
942
942
943 Now the directory is eligible for caching, so its mtime is save in the dirstate
943 Now the directory is eligible for caching, so its mtime is save in the dirstate
944
944
945 $ rm subdir/unknown
945 $ rm subdir/unknown
946 $ hg status
946 $ hg status
947 $ hg debugdirstate --dirs --no-dates | grep '^d'
947 $ hg debugdirstate --all --no-dates | grep '^ '
948 d 0 0 set subdir
948 0 -1 set subdir
949
949
950 This time the command should be ever so slightly faster since it does not need `read_dir("subdir")`
950 This time the command should be ever so slightly faster since it does not need `read_dir("subdir")`
951
951
@@ -963,11 +963,11 b' Creating a new file changes the directory\xe2\x80\x99s mtime, invalidating the cache'
963 Removing a node from the dirstate resets the cache for its parent directory
963 Removing a node from the dirstate resets the cache for its parent directory
964
964
965 $ hg forget subdir/a
965 $ hg forget subdir/a
966 $ hg debugdirstate --dirs --no-dates | grep '^d'
966 $ hg debugdirstate --all --no-dates | grep '^ '
967 d 0 0 set subdir
967 0 -1 set subdir
968 $ hg ci -qm '#1'
968 $ hg ci -qm '#1'
969 $ hg debugdirstate --dirs --no-dates | grep '^d'
969 $ hg debugdirstate --all --no-dates | grep '^ '
970 d 0 0 unset subdir
970 0 -1 unset subdir
971 $ hg status
971 $ hg status
972 ? subdir/a
972 ? subdir/a
973
973
General Comments 0
You need to be logged in to leave comments. Login now