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