##// END OF EJS Templates
dirstate-tree: Add `NodeRef` and `ChildNodesRef` enums...
Simon Sapin -
r48124:69530e5d default
parent child Browse files
Show More
@@ -45,8 +45,160 b" pub struct DirstateMap<'on_disk> {"
45 45 /// names. However `HashMap` would waste time always re-hashing the same
46 46 /// string prefix.
47 47 pub(super) type NodeKey<'on_disk> = WithBasename<Cow<'on_disk, HgPath>>;
48 pub(super) type ChildNodes<'on_disk> =
49 FastHashMap<NodeKey<'on_disk>, Node<'on_disk>>;
48
49 pub(super) enum ChildNodes<'on_disk> {
50 InMemory(FastHashMap<NodeKey<'on_disk>, Node<'on_disk>>),
51 }
52
53 pub(super) enum ChildNodesRef<'tree, 'on_disk> {
54 InMemory(&'tree FastHashMap<NodeKey<'on_disk>, Node<'on_disk>>),
55 }
56
57 pub(super) enum NodeRef<'tree, 'on_disk> {
58 InMemory(&'tree NodeKey<'on_disk>, &'tree Node<'on_disk>),
59 }
60
61 impl Default for ChildNodes<'_> {
62 fn default() -> Self {
63 ChildNodes::InMemory(Default::default())
64 }
65 }
66
67 impl<'on_disk> ChildNodes<'on_disk> {
68 pub(super) fn as_ref<'tree>(
69 &'tree self,
70 ) -> ChildNodesRef<'tree, 'on_disk> {
71 match self {
72 ChildNodes::InMemory(nodes) => ChildNodesRef::InMemory(nodes),
73 }
74 }
75
76 pub(super) fn is_empty(&self) -> bool {
77 match self {
78 ChildNodes::InMemory(nodes) => nodes.is_empty(),
79 }
80 }
81
82 pub(super) fn make_mut(
83 &mut self,
84 ) -> &mut FastHashMap<NodeKey<'on_disk>, Node<'on_disk>> {
85 match self {
86 ChildNodes::InMemory(nodes) => nodes,
87 }
88 }
89 }
90
91 impl<'tree, 'on_disk> ChildNodesRef<'tree, 'on_disk> {
92 pub(super) fn get(
93 &self,
94 base_name: &HgPath,
95 ) -> Option<NodeRef<'tree, 'on_disk>> {
96 match self {
97 ChildNodesRef::InMemory(nodes) => nodes
98 .get_key_value(base_name)
99 .map(|(k, v)| NodeRef::InMemory(k, v)),
100 }
101 }
102
103 /// Iterate in undefined order
104 pub(super) fn iter(
105 &self,
106 ) -> impl Iterator<Item = NodeRef<'tree, 'on_disk>> {
107 match self {
108 ChildNodesRef::InMemory(nodes) => {
109 nodes.iter().map(|(k, v)| NodeRef::InMemory(k, v))
110 }
111 }
112 }
113
114 /// Iterate in parallel in undefined order
115 pub(super) fn par_iter(
116 &self,
117 ) -> impl rayon::iter::ParallelIterator<Item = NodeRef<'tree, 'on_disk>>
118 {
119 use rayon::prelude::*;
120 match self {
121 ChildNodesRef::InMemory(nodes) => {
122 nodes.par_iter().map(|(k, v)| NodeRef::InMemory(k, v))
123 }
124 }
125 }
126
127 pub(super) fn sorted(&self) -> Vec<NodeRef<'tree, 'on_disk>> {
128 match self {
129 ChildNodesRef::InMemory(nodes) => {
130 let mut vec: Vec<_> = nodes
131 .iter()
132 .map(|(k, v)| NodeRef::InMemory(k, v))
133 .collect();
134 // `sort_unstable_by_key` doesn’t allow keys borrowing from the
135 // value: https://github.com/rust-lang/rust/issues/34162
136 vec.sort_unstable_by(|a, b| a.base_name().cmp(b.base_name()));
137 vec
138 }
139 }
140 }
141 }
142
143 impl<'tree, 'on_disk> NodeRef<'tree, 'on_disk> {
144 pub(super) fn full_path(&self) -> &'tree HgPath {
145 match self {
146 NodeRef::InMemory(path, _node) => path.full_path(),
147 }
148 }
149
150 /// Returns a `Cow` that can borrow 'on_disk but is detached from 'tree
151 pub(super) fn full_path_cow(&self) -> Cow<'on_disk, HgPath> {
152 match self {
153 NodeRef::InMemory(path, _node) => path.full_path().clone(),
154 }
155 }
156
157 pub(super) fn base_name(&self) -> &'tree HgPath {
158 match self {
159 NodeRef::InMemory(path, _node) => path.base_name(),
160 }
161 }
162
163 pub(super) fn children(&self) -> ChildNodesRef<'tree, 'on_disk> {
164 match self {
165 NodeRef::InMemory(_path, node) => node.children.as_ref(),
166 }
167 }
168
169 pub(super) fn copy_source(&self) -> Option<&'tree HgPath> {
170 match self {
171 NodeRef::InMemory(_path, node) => {
172 node.copy_source.as_ref().map(|s| &**s)
173 }
174 }
175 }
176
177 pub(super) fn has_entry(&self) -> bool {
178 match self {
179 NodeRef::InMemory(_path, node) => node.entry.is_some(),
180 }
181 }
182
183 pub(super) fn entry(&self) -> Option<DirstateEntry> {
184 match self {
185 NodeRef::InMemory(_path, node) => node.entry,
186 }
187 }
188 pub(super) fn state(&self) -> Option<EntryState> {
189 match self {
190 NodeRef::InMemory(_path, node) => {
191 node.entry.as_ref().map(|entry| entry.state)
192 }
193 }
194 }
195
196 pub(super) fn tracked_descendants_count(&self) -> u32 {
197 match self {
198 NodeRef::InMemory(_path, node) => node.tracked_descendants_count,
199 }
200 }
201 }
50 202
51 203 /// Represents a file or a directory
52 204 #[derive(Default)]
@@ -62,22 +214,6 b" pub(super) struct Node<'on_disk> {"
62 214 pub(super) tracked_descendants_count: u32,
63 215 }
64 216
65 impl<'on_disk> Node<'on_disk> {
66 pub(super) fn state(&self) -> Option<EntryState> {
67 self.entry.as_ref().map(|entry| entry.state)
68 }
69
70 pub(super) fn sorted<'tree>(
71 nodes: &'tree ChildNodes<'on_disk>,
72 ) -> Vec<(&'tree NodeKey<'on_disk>, &'tree Self)> {
73 let mut vec: Vec<_> = nodes.iter().collect();
74 // `sort_unstable_by_key` doesn’t allow keys borrowing from the value:
75 // https://github.com/rust-lang/rust/issues/34162
76 vec.sort_unstable_by(|(path1, _), (path2, _)| path1.cmp(path2));
77 vec
78 }
79 }
80
81 217 impl<'on_disk> DirstateMap<'on_disk> {
82 218 pub(super) fn empty(on_disk: &'on_disk [u8]) -> Self {
83 219 Self {
@@ -139,8 +275,11 b" impl<'on_disk> DirstateMap<'on_disk> {"
139 275 Ok((map, parents))
140 276 }
141 277
142 fn get_node(&self, path: &HgPath) -> Option<&Node> {
143 let mut children = &self.root;
278 fn get_node<'tree>(
279 &'tree self,
280 path: &HgPath,
281 ) -> Option<NodeRef<'tree, 'on_disk>> {
282 let mut children = self.root.as_ref();
144 283 let mut components = path.components();
145 284 let mut component =
146 285 components.next().expect("expected at least one components");
@@ -148,7 +287,7 b" impl<'on_disk> DirstateMap<'on_disk> {"
148 287 let child = children.get(component)?;
149 288 if let Some(next_component) = components.next() {
150 289 component = next_component;
151 children = &child.children;
290 children = child.children();
152 291 } else {
153 292 return Some(child);
154 293 }
@@ -168,7 +307,7 b" impl<'on_disk> DirstateMap<'on_disk> {"
168 307 let mut component =
169 308 components.next().expect("expected at least one components");
170 309 loop {
171 let child = children.get_mut(component)?;
310 let child = children.make_mut().get_mut(component)?;
172 311 if let Some(next_component) = components.next() {
173 312 component = next_component;
174 313 children = &mut child.children;
@@ -196,8 +335,10 b" impl<'on_disk> DirstateMap<'on_disk> {"
196 335 // TODO: can we avoid allocating an owned key in cases where the
197 336 // map already contains that key, without introducing double
198 337 // lookup?
199 let child_node =
200 child_nodes.entry(to_cow(ancestor_path)).or_default();
338 let child_node = child_nodes
339 .make_mut()
340 .entry(to_cow(ancestor_path))
341 .or_default();
201 342 if let Some(next) = inclusive_ancestor_paths.next() {
202 343 each_ancestor(child_node);
203 344 ancestor_path = next;
@@ -242,9 +383,9 b" impl<'on_disk> DirstateMap<'on_disk> {"
242 383 node.entry = Some(new_entry)
243 384 }
244 385
245 fn iter_nodes<'a>(
246 &'a self,
247 ) -> impl Iterator<Item = (&'a Cow<'on_disk, HgPath>, &'a Node)> + 'a {
386 fn iter_nodes<'tree>(
387 &'tree self,
388 ) -> impl Iterator<Item = NodeRef<'tree, 'on_disk>> + 'tree {
248 389 // Depth first tree traversal.
249 390 //
250 391 // If we could afford internal iteration and recursion,
@@ -265,22 +406,21 b" impl<'on_disk> DirstateMap<'on_disk> {"
265 406 // However we want an external iterator and therefore can’t use the
266 407 // call stack. Use an explicit stack instead:
267 408 let mut stack = Vec::new();
268 let mut iter = self.root.iter();
409 let mut iter = self.root.as_ref().iter();
269 410 std::iter::from_fn(move || {
270 while let Some((key, child_node)) = iter.next() {
411 while let Some(child_node) = iter.next() {
271 412 // Pseudo-recursion
272 let new_iter = child_node.children.iter();
413 let new_iter = child_node.children().iter();
273 414 let old_iter = std::mem::replace(&mut iter, new_iter);
274 let key = key.full_path();
275 stack.push((key, child_node, old_iter));
415 stack.push((child_node, old_iter));
276 416 }
277 417 // Found the end of a `children.iter()` iterator.
278 if let Some((key, child_node, next_iter)) = stack.pop() {
418 if let Some((child_node, next_iter)) = stack.pop() {
279 419 // "Return" from pseudo-recursion by restoring state from the
280 420 // explicit stack
281 421 iter = next_iter;
282 422
283 Some((key, child_node))
423 Some(child_node)
284 424 } else {
285 425 // Reached the bottom of the stack, we’re done
286 426 None
@@ -303,7 +443,7 b" impl<'on_disk> DirstateMap<'on_disk> {"
303 443
304 444 impl<'on_disk> super::dispatch::DirstateMapMethods for DirstateMap<'on_disk> {
305 445 fn clear(&mut self) {
306 self.root.clear();
446 self.root = Default::default();
307 447 self.nodes_with_entry_count = 0;
308 448 self.nodes_with_copy_source_count = 0;
309 449 }
@@ -347,7 +487,7 b" impl<'on_disk> super::dispatch::Dirstate"
347 487 fn recur(nodes: &mut ChildNodes, path: &HgPath) -> Option<Dropped> {
348 488 let (first_path_component, rest_of_path) =
349 489 path.split_first_component();
350 let node = nodes.get_mut(first_path_component)?;
490 let node = nodes.make_mut().get_mut(first_path_component)?;
351 491 let dropped;
352 492 if let Some(rest) = rest_of_path {
353 493 dropped = recur(&mut node.children, rest)?;
@@ -370,7 +510,7 b" impl<'on_disk> super::dispatch::Dirstate"
370 510 && node.copy_source.is_none()
371 511 && node.children.is_empty()
372 512 {
373 nodes.remove(first_path_component);
513 nodes.make_mut().remove(first_path_component);
374 514 }
375 515 Some(dropped)
376 516 }
@@ -401,8 +541,8 b" impl<'on_disk> super::dispatch::Dirstate"
401 541
402 542 fn non_normal_entries_contains(&mut self, key: &HgPath) -> bool {
403 543 self.get_node(key)
404 .and_then(|node| node.entry.as_ref())
405 .map_or(false, DirstateEntry::is_non_normal)
544 .and_then(|node| node.entry())
545 .map_or(false, |entry| entry.is_non_normal())
406 546 }
407 547
408 548 fn non_normal_entries_remove(&mut self, _key: &HgPath) {
@@ -413,13 +553,12 b" impl<'on_disk> super::dispatch::Dirstate"
413 553 fn non_normal_or_other_parent_paths(
414 554 &mut self,
415 555 ) -> Box<dyn Iterator<Item = &HgPath> + '_> {
416 Box::new(self.iter_nodes().filter_map(|(path, node)| {
417 node.entry
418 .as_ref()
556 Box::new(self.iter_nodes().filter_map(|node| {
557 node.entry()
419 558 .filter(|entry| {
420 559 entry.is_non_normal() || entry.is_from_other_parent()
421 560 })
422 .map(|_| &**path)
561 .map(|_| node.full_path())
423 562 }))
424 563 }
425 564
@@ -437,22 +576,20 b" impl<'on_disk> super::dispatch::Dirstate"
437 576 fn iter_non_normal_paths_panic(
438 577 &self,
439 578 ) -> Box<dyn Iterator<Item = &HgPath> + Send + '_> {
440 Box::new(self.iter_nodes().filter_map(|(path, node)| {
441 node.entry
442 .as_ref()
579 Box::new(self.iter_nodes().filter_map(|node| {
580 node.entry()
443 581 .filter(|entry| entry.is_non_normal())
444 .map(|_| &**path)
582 .map(|_| node.full_path())
445 583 }))
446 584 }
447 585
448 586 fn iter_other_parent_paths(
449 587 &mut self,
450 588 ) -> Box<dyn Iterator<Item = &HgPath> + Send + '_> {
451 Box::new(self.iter_nodes().filter_map(|(path, node)| {
452 node.entry
453 .as_ref()
589 Box::new(self.iter_nodes().filter_map(|node| {
590 node.entry()
454 591 .filter(|entry| entry.is_from_other_parent())
455 .map(|_| &**path)
592 .map(|_| node.full_path())
456 593 }))
457 594 }
458 595
@@ -463,7 +600,7 b" impl<'on_disk> super::dispatch::Dirstate"
463 600 if let Some(node) = self.get_node(directory) {
464 601 // A node without a `DirstateEntry` was created to hold child
465 602 // nodes, and is therefore a directory.
466 Ok(node.entry.is_none() && node.tracked_descendants_count > 0)
603 Ok(!node.has_entry() && node.tracked_descendants_count() > 0)
467 604 } else {
468 605 Ok(false)
469 606 }
@@ -476,7 +613,7 b" impl<'on_disk> super::dispatch::Dirstate"
476 613 if let Some(node) = self.get_node(directory) {
477 614 // A node without a `DirstateEntry` was created to hold child
478 615 // nodes, and is therefore a directory.
479 Ok(node.entry.is_none())
616 Ok(!node.has_entry())
480 617 } else {
481 618 Ok(false)
482 619 }
@@ -493,14 +630,12 b" impl<'on_disk> super::dispatch::Dirstate"
493 630 // Optizimation (to be measured?): pre-compute size to avoid `Vec`
494 631 // reallocations
495 632 let mut size = parents.as_bytes().len();
496 for (path, node) in self.iter_nodes() {
497 if let Some(entry) = &node.entry {
498 size += packed_entry_size(
499 path,
500 node.copy_source.as_ref().map(|p| &**p),
501 );
633 for node in self.iter_nodes() {
634 if let Some(entry) = node.entry() {
635 size +=
636 packed_entry_size(node.full_path(), node.copy_source());
502 637 if entry.mtime_is_ambiguous(now) {
503 ambiguous_mtimes.push(path.clone())
638 ambiguous_mtimes.push(node.full_path_cow())
504 639 }
505 640 }
506 641 }
@@ -509,12 +644,12 b" impl<'on_disk> super::dispatch::Dirstate"
509 644 let mut packed = Vec::with_capacity(size);
510 645 packed.extend(parents.as_bytes());
511 646
512 for (path, node) in self.iter_nodes() {
513 if let Some(entry) = &node.entry {
647 for node in self.iter_nodes() {
648 if let Some(entry) = node.entry() {
514 649 pack_entry(
515 path,
516 entry,
517 node.copy_source.as_ref().map(|p| &**p),
650 node.full_path(),
651 &entry,
652 node.copy_source(),
518 653 &mut packed,
519 654 );
520 655 }
@@ -531,10 +666,10 b" impl<'on_disk> super::dispatch::Dirstate"
531 666 // TODO:Β how do we want to handle this in 2038?
532 667 let now: i32 = now.0.try_into().expect("time overflow");
533 668 let mut paths = Vec::new();
534 for (path, node) in self.iter_nodes() {
535 if let Some(entry) = &node.entry {
669 for node in self.iter_nodes() {
670 if let Some(entry) = node.entry() {
536 671 if entry.mtime_is_ambiguous(now) {
537 paths.push(path.clone())
672 paths.push(node.full_path_cow())
538 673 }
539 674 }
540 675 }
@@ -573,23 +708,22 b" impl<'on_disk> super::dispatch::Dirstate"
573 708 }
574 709
575 710 fn copy_map_iter(&self) -> CopyMapIter<'_> {
576 Box::new(self.iter_nodes().filter_map(|(path, node)| {
577 node.copy_source
578 .as_ref()
579 .map(|copy_source| (&**path, &**copy_source))
711 Box::new(self.iter_nodes().filter_map(|node| {
712 node.copy_source()
713 .map(|copy_source| (node.full_path(), copy_source))
580 714 }))
581 715 }
582 716
583 717 fn copy_map_contains_key(&self, key: &HgPath) -> bool {
584 718 if let Some(node) = self.get_node(key) {
585 node.copy_source.is_some()
719 node.copy_source().is_some()
586 720 } else {
587 721 false
588 722 }
589 723 }
590 724
591 725 fn copy_map_get(&self, key: &HgPath) -> Option<&HgPath> {
592 self.get_node(key)?.copy_source.as_ref().map(|p| &**p)
726 self.get_node(key)?.copy_source()
593 727 }
594 728
595 729 fn copy_map_remove(&mut self, key: &HgPath) -> Option<HgPathBuf> {
@@ -628,12 +762,12 b" impl<'on_disk> super::dispatch::Dirstate"
628 762 }
629 763
630 764 fn get(&self, key: &HgPath) -> Option<DirstateEntry> {
631 self.get_node(key)?.entry
765 self.get_node(key)?.entry()
632 766 }
633 767
634 768 fn iter(&self) -> StateMapIter<'_> {
635 Box::new(self.iter_nodes().filter_map(|(path, node)| {
636 node.entry.map(|entry| (&**path, entry))
769 Box::new(self.iter_nodes().filter_map(|node| {
770 node.entry().map(|entry| (node.full_path(), entry))
637 771 }))
638 772 }
639 773 }
@@ -9,7 +9,7 b''
9 9 //! Nodes in turn contain slices to variable-size paths, and to their own child
10 10 //! nodes (if any) for nested files and directories.
11 11
12 use crate::dirstate_tree::dirstate_map::{self, DirstateMap};
12 use crate::dirstate_tree::dirstate_map::{self, DirstateMap, NodeRef};
13 13 use crate::dirstate_tree::path_with_basename::WithBasename;
14 14 use crate::errors::HgError;
15 15 use crate::utils::hg_path::HgPath;
@@ -200,7 +200,8 b' fn read_nodes('
200 200 .map(|node| {
201 201 Ok((node.path(on_disk)?, node.to_in_memory_node(on_disk)?))
202 202 })
203 .collect()
203 .collect::<Result<_, _>>()
204 .map(dirstate_map::ChildNodes::InMemory)
204 205 }
205 206
206 207 fn read_hg_path(on_disk: &[u8], slice: Slice) -> Result<Cow<HgPath>, HgError> {
@@ -242,7 +243,7 b' pub(super) fn write('
242 243 // actual offset for the root nodes.
243 244 out.resize(header_len, 0_u8);
244 245
245 let root = write_nodes(&mut dirstate_map.root, &mut out)?;
246 let root = write_nodes(dirstate_map.root.as_ref(), &mut out)?;
246 247
247 248 let header = Header {
248 249 marker: *V2_FORMAT_MARKER,
@@ -258,49 +259,53 b' pub(super) fn write('
258 259 }
259 260
260 261 fn write_nodes(
261 nodes: &dirstate_map::ChildNodes,
262 nodes: dirstate_map::ChildNodesRef,
262 263 out: &mut Vec<u8>,
263 264 ) -> Result<ChildNodes, DirstateError> {
264 265 // `dirstate_map::ChildNodes` is a `HashMap` with undefined iteration
265 266 // order. Sort to enable binary search in the written file.
266 let nodes = dirstate_map::Node::sorted(nodes);
267 let nodes = nodes.sorted();
267 268
268 269 // First accumulate serialized nodes in a `Vec`
269 270 let mut on_disk_nodes = Vec::with_capacity(nodes.len());
270 for (full_path, node) in nodes {
271 on_disk_nodes.push(Node {
272 children: write_nodes(&node.children, out)?,
273 tracked_descendants_count: node.tracked_descendants_count.into(),
274 full_path: write_slice::<u8>(
275 full_path.full_path().as_bytes(),
276 out,
277 ),
278 base_name_start: u32::try_from(full_path.base_name_start())
279 // Could only panic for paths over 4 GiB
280 .expect("dirstate-v2 offset overflow")
281 .into(),
282 copy_source: if let Some(source) = &node.copy_source {
283 write_slice::<u8>(source.as_bytes(), out)
284 } else {
285 Slice {
286 start: 0.into(),
287 len: 0.into(),
288 }
289 },
290 entry: if let Some(entry) = &node.entry {
291 OptEntry {
292 state: entry.state.into(),
293 mode: entry.mode.into(),
294 mtime: entry.mtime.into(),
295 size: entry.size.into(),
296 }
297 } else {
298 OptEntry {
299 state: b'\0',
300 mode: 0.into(),
301 mtime: 0.into(),
302 size: 0.into(),
303 }
271 for node in nodes {
272 let children = write_nodes(node.children(), out)?;
273 let full_path = write_slice::<u8>(node.full_path().as_bytes(), out);
274 let copy_source = if let Some(source) = node.copy_source() {
275 write_slice::<u8>(source.as_bytes(), out)
276 } else {
277 Slice {
278 start: 0.into(),
279 len: 0.into(),
280 }
281 };
282 on_disk_nodes.push(match node {
283 NodeRef::InMemory(path, node) => Node {
284 children,
285 copy_source,
286 full_path,
287 base_name_start: u32::try_from(path.base_name_start())
288 // Could only panic for paths over 4 GiB
289 .expect("dirstate-v2 offset overflow")
290 .into(),
291 tracked_descendants_count: node
292 .tracked_descendants_count
293 .into(),
294 entry: if let Some(entry) = &node.entry {
295 OptEntry {
296 state: entry.state.into(),
297 mode: entry.mode.into(),
298 mtime: entry.mtime.into(),
299 size: entry.size.into(),
300 }
301 } else {
302 OptEntry {
303 state: b'\0',
304 mode: 0.into(),
305 mtime: 0.into(),
306 size: 0.into(),
307 }
308 },
304 309 },
305 310 })
306 311 }
@@ -1,7 +1,7 b''
1 1 use crate::dirstate::status::IgnoreFnType;
2 use crate::dirstate_tree::dirstate_map::ChildNodes;
2 use crate::dirstate_tree::dirstate_map::ChildNodesRef;
3 3 use crate::dirstate_tree::dirstate_map::DirstateMap;
4 use crate::dirstate_tree::dirstate_map::Node;
4 use crate::dirstate_tree::dirstate_map::NodeRef;
5 5 use crate::matchers::get_ignore_function;
6 6 use crate::matchers::Matcher;
7 7 use crate::utils::files::get_bytes_from_os_string;
@@ -56,7 +56,7 b" pub fn status<'tree>("
56 56 let has_ignored_ancestor = false;
57 57 common.traverse_fs_directory_and_dirstate(
58 58 has_ignored_ancestor,
59 &dmap.root,
59 dmap.root.as_ref(),
60 60 hg_path,
61 61 &root_dir,
62 62 is_at_repo_root,
@@ -93,7 +93,7 b" impl<'tree, 'a> StatusCommon<'tree, 'a> "
93 93 fn traverse_fs_directory_and_dirstate(
94 94 &self,
95 95 has_ignored_ancestor: bool,
96 dirstate_nodes: &'tree ChildNodes,
96 dirstate_nodes: ChildNodesRef<'tree, '_>,
97 97 directory_hg_path: &'tree HgPath,
98 98 directory_fs_path: &Path,
99 99 is_at_repo_root: bool,
@@ -110,7 +110,7 b" impl<'tree, 'a> StatusCommon<'tree, 'a> "
110 110
111 111 // `merge_join_by` requires both its input iterators to be sorted:
112 112
113 let dirstate_nodes = Node::sorted(dirstate_nodes);
113 let dirstate_nodes = dirstate_nodes.sorted();
114 114 // `sort_unstable_by_key` doesn’t allow keys borrowing from the value:
115 115 // https://github.com/rust-lang/rust/issues/34162
116 116 fs_entries.sort_unstable_by(|e1, e2| e1.base_name.cmp(&e2.base_name));
@@ -118,26 +118,24 b" impl<'tree, 'a> StatusCommon<'tree, 'a> "
118 118 itertools::merge_join_by(
119 119 dirstate_nodes,
120 120 &fs_entries,
121 |(full_path, _node), fs_entry| {
122 full_path.base_name().cmp(&fs_entry.base_name)
121 |dirstate_node, fs_entry| {
122 dirstate_node.base_name().cmp(&fs_entry.base_name)
123 123 },
124 124 )
125 125 .par_bridge()
126 126 .for_each(|pair| {
127 127 use itertools::EitherOrBoth::*;
128 128 match pair {
129 Both((hg_path, dirstate_node), fs_entry) => {
129 Both(dirstate_node, fs_entry) => {
130 130 self.traverse_fs_and_dirstate(
131 131 fs_entry,
132 hg_path.full_path(),
133 132 dirstate_node,
134 133 has_ignored_ancestor,
135 134 );
136 135 }
137 Left((hg_path, dirstate_node)) => self.traverse_dirstate_only(
138 hg_path.full_path(),
139 dirstate_node,
140 ),
136 Left(dirstate_node) => {
137 self.traverse_dirstate_only(dirstate_node)
138 }
141 139 Right(fs_entry) => self.traverse_fs_only(
142 140 has_ignored_ancestor,
143 141 directory_hg_path,
@@ -150,10 +148,10 b" impl<'tree, 'a> StatusCommon<'tree, 'a> "
150 148 fn traverse_fs_and_dirstate(
151 149 &self,
152 150 fs_entry: &DirEntry,
153 hg_path: &'tree HgPath,
154 dirstate_node: &'tree Node,
151 dirstate_node: NodeRef<'tree, '_>,
155 152 has_ignored_ancestor: bool,
156 153 ) {
154 let hg_path = dirstate_node.full_path();
157 155 let file_type = fs_entry.metadata.file_type();
158 156 let file_or_symlink = file_type.is_file() || file_type.is_symlink();
159 157 if !file_or_symlink {
@@ -161,7 +159,7 b" impl<'tree, 'a> StatusCommon<'tree, 'a> "
161 159 // `hg rm` or similar) or deleted before it could be
162 160 // replaced by a directory or something else.
163 161 self.mark_removed_or_deleted_if_file(
164 hg_path,
162 dirstate_node.full_path(),
165 163 dirstate_node.state(),
166 164 );
167 165 }
@@ -173,7 +171,7 b" impl<'tree, 'a> StatusCommon<'tree, 'a> "
173 171 let is_at_repo_root = false;
174 172 self.traverse_fs_directory_and_dirstate(
175 173 is_ignored,
176 &dirstate_node.children,
174 dirstate_node.children(),
177 175 hg_path,
178 176 &fs_entry.full_path,
179 177 is_at_repo_root,
@@ -181,8 +179,8 b" impl<'tree, 'a> StatusCommon<'tree, 'a> "
181 179 } else {
182 180 if file_or_symlink && self.matcher.matches(hg_path) {
183 181 let full_path = Cow::from(hg_path);
184 if let Some(entry) = &dirstate_node.entry {
185 match entry.state {
182 if let Some(state) = dirstate_node.state() {
183 match state {
186 184 EntryState::Added => {
187 185 self.outcome.lock().unwrap().added.push(full_path)
188 186 }
@@ -199,12 +197,7 b" impl<'tree, 'a> StatusCommon<'tree, 'a> "
199 197 .modified
200 198 .push(full_path),
201 199 EntryState::Normal => {
202 self.handle_normal_file(
203 full_path,
204 dirstate_node,
205 entry,
206 fs_entry,
207 );
200 self.handle_normal_file(&dirstate_node, fs_entry);
208 201 }
209 202 // This variant is not used in DirstateMap
210 203 // nodes
@@ -220,11 +213,8 b" impl<'tree, 'a> StatusCommon<'tree, 'a> "
220 213 }
221 214 }
222 215
223 for (child_hg_path, child_node) in &dirstate_node.children {
224 self.traverse_dirstate_only(
225 child_hg_path.full_path(),
226 child_node,
227 )
216 for child_node in dirstate_node.children().iter() {
217 self.traverse_dirstate_only(child_node)
228 218 }
229 219 }
230 220 }
@@ -233,9 +223,7 b" impl<'tree, 'a> StatusCommon<'tree, 'a> "
233 223 /// filesystem
234 224 fn handle_normal_file(
235 225 &self,
236 full_path: Cow<'tree, HgPath>,
237 dirstate_node: &Node,
238 entry: &crate::DirstateEntry,
226 dirstate_node: &NodeRef<'tree, '_>,
239 227 fs_entry: &DirEntry,
240 228 ) {
241 229 // Keep the low 31 bits
@@ -246,6 +234,10 b" impl<'tree, 'a> StatusCommon<'tree, 'a> "
246 234 (value & 0x7FFF_FFFF) as i32
247 235 }
248 236
237 let entry = dirstate_node
238 .entry()
239 .expect("handle_normal_file called with entry-less node");
240 let full_path = Cow::from(dirstate_node.full_path());
249 241 let mode_changed = || {
250 242 self.options.check_exec && entry.mode_changed(&fs_entry.metadata)
251 243 };
@@ -257,7 +249,7 b" impl<'tree, 'a> StatusCommon<'tree, 'a> "
257 249 // issue6456: Size returned may be longer due to encryption
258 250 // on EXT-4 fscrypt. TODO maybe only do it on EXT4?
259 251 self.outcome.lock().unwrap().unsure.push(full_path)
260 } else if dirstate_node.copy_source.is_some()
252 } else if dirstate_node.copy_source().is_some()
261 253 || entry.is_from_other_parent()
262 254 || (entry.size >= 0 && (size_changed || mode_changed()))
263 255 {
@@ -275,20 +267,15 b" impl<'tree, 'a> StatusCommon<'tree, 'a> "
275 267 }
276 268
277 269 /// A node in the dirstate tree has no corresponding filesystem entry
278 fn traverse_dirstate_only(
279 &self,
280 hg_path: &'tree HgPath,
281 dirstate_node: &'tree Node,
282 ) {
283 self.mark_removed_or_deleted_if_file(hg_path, dirstate_node.state());
284 dirstate_node.children.par_iter().for_each(
285 |(child_hg_path, child_node)| {
286 self.traverse_dirstate_only(
287 child_hg_path.full_path(),
288 child_node,
289 )
290 },
291 )
270 fn traverse_dirstate_only(&self, dirstate_node: NodeRef<'tree, '_>) {
271 self.mark_removed_or_deleted_if_file(
272 dirstate_node.full_path(),
273 dirstate_node.state(),
274 );
275 dirstate_node
276 .children()
277 .par_iter()
278 .for_each(|child_node| self.traverse_dirstate_only(child_node))
292 279 }
293 280
294 281 /// A node in the dirstate tree has no corresponding *file* on the
General Comments 0
You need to be logged in to leave comments. Login now