2 use micro_timer::timed; |
2 use micro_timer::timed; |
3 use std::borrow::Cow; |
3 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::on_disk; |
8 use super::path_with_basename::WithBasename; |
8 use super::path_with_basename::WithBasename; |
9 use crate::dirstate::parsers::clear_ambiguous_mtime; |
9 use crate::dirstate::parsers::clear_ambiguous_mtime; |
10 use crate::dirstate::parsers::pack_entry; |
10 use crate::dirstate::parsers::pack_entry; |
11 use crate::dirstate::parsers::packed_entry_size; |
11 use crate::dirstate::parsers::packed_entry_size; |
12 use crate::dirstate::parsers::parse_dirstate_entries; |
12 use crate::dirstate::parsers::parse_dirstate_entries; |
13 use crate::dirstate::parsers::Timestamp; |
13 use crate::dirstate::parsers::Timestamp; |
14 use crate::errors::HgError; |
|
15 use crate::matchers::Matcher; |
14 use crate::matchers::Matcher; |
16 use crate::utils::hg_path::{HgPath, HgPathBuf}; |
15 use crate::utils::hg_path::{HgPath, HgPathBuf}; |
17 use crate::utils::SliceExt; |
|
18 use crate::CopyMapIter; |
16 use crate::CopyMapIter; |
19 use crate::DirstateEntry; |
17 use crate::DirstateEntry; |
20 use crate::DirstateError; |
18 use crate::DirstateError; |
21 use crate::DirstateMapError; |
19 use crate::DirstateMapError; |
22 use crate::DirstateParents; |
20 use crate::DirstateParents; |
28 use crate::StatusError; |
26 use crate::StatusError; |
29 use crate::StatusOptions; |
27 use crate::StatusOptions; |
30 |
28 |
31 pub struct DirstateMap<'on_disk> { |
29 pub struct DirstateMap<'on_disk> { |
32 /// Contents of the `.hg/dirstate` file |
30 /// Contents of the `.hg/dirstate` file |
33 on_disk: &'on_disk [u8], |
31 pub(super) on_disk: &'on_disk [u8], |
34 |
32 |
35 pub(super) root: ChildNodes<'on_disk>, |
33 pub(super) root: ChildNodes<'on_disk>, |
36 |
34 |
37 /// Number of nodes anywhere in the tree that have `.entry.is_some()`. |
35 /// Number of nodes anywhere in the tree that have `.entry.is_some()`. |
38 nodes_with_entry_count: usize, |
36 pub(super) nodes_with_entry_count: u32, |
39 |
37 |
40 /// Number of nodes anywhere in the tree that have |
38 /// Number of nodes anywhere in the tree that have |
41 /// `.copy_source.is_some()`. |
39 /// `.copy_source.is_some()`. |
42 nodes_with_copy_source_count: usize, |
40 pub(super) nodes_with_copy_source_count: u32, |
43 } |
41 } |
44 |
42 |
45 /// Using a plain `HgPathBuf` of the full path from the repository root as a |
43 /// Using a plain `HgPathBuf` of the full path from the repository root as a |
46 /// map key would also work: all paths in a given map have the same parent |
44 /// map key would also work: all paths in a given map have the same parent |
47 /// path, so comparing full paths gives the same result as comparing base |
45 /// path, so comparing full paths gives the same result as comparing base |
60 pub(super) copy_source: Option<Cow<'on_disk, HgPath>>, |
58 pub(super) copy_source: Option<Cow<'on_disk, HgPath>>, |
61 |
59 |
62 pub(super) children: ChildNodes<'on_disk>, |
60 pub(super) children: ChildNodes<'on_disk>, |
63 |
61 |
64 /// How many (non-inclusive) descendants of this node are tracked files |
62 /// How many (non-inclusive) descendants of this node are tracked files |
65 tracked_descendants_count: usize, |
63 pub(super) tracked_descendants_count: u32, |
66 } |
64 } |
67 |
65 |
68 impl<'on_disk> Node<'on_disk> { |
66 impl<'on_disk> Node<'on_disk> { |
69 pub(super) fn state(&self) -> Option<EntryState> { |
67 pub(super) fn state(&self) -> Option<EntryState> { |
70 self.entry.as_ref().map(|entry| entry.state) |
68 self.entry.as_ref().map(|entry| entry.state) |
87 &'tree mut Option<DirstateEntry>, |
85 &'tree mut Option<DirstateEntry>, |
88 &'tree mut Option<Cow<'on_disk, HgPath>>, |
86 &'tree mut Option<Cow<'on_disk, HgPath>>, |
89 ); |
87 ); |
90 |
88 |
91 impl<'on_disk> DirstateMap<'on_disk> { |
89 impl<'on_disk> DirstateMap<'on_disk> { |
|
90 pub(super) fn empty(on_disk: &'on_disk [u8]) -> Self { |
|
91 Self { |
|
92 on_disk, |
|
93 root: ChildNodes::default(), |
|
94 nodes_with_entry_count: 0, |
|
95 nodes_with_copy_source_count: 0, |
|
96 } |
|
97 } |
|
98 |
92 #[timed] |
99 #[timed] |
93 pub fn new_v2( |
100 pub fn new_v2( |
94 on_disk: &'on_disk [u8], |
101 on_disk: &'on_disk [u8], |
95 ) -> Result<(Self, Option<DirstateParents>), DirstateError> { |
102 ) -> Result<(Self, Option<DirstateParents>), DirstateError> { |
96 if let Some(rest) = on_disk.drop_prefix(V2_FORMAT_MARKER) { |
103 on_disk::read(on_disk) |
97 Self::new_v1(rest) |
|
98 } else if on_disk.is_empty() { |
|
99 Self::new_v1(on_disk) |
|
100 } else { |
|
101 return Err(HgError::corrupted( |
|
102 "missing dirstate-v2 magic number", |
|
103 ) |
|
104 .into()); |
|
105 } |
|
106 } |
104 } |
107 |
105 |
108 #[timed] |
106 #[timed] |
109 pub fn new_v1( |
107 pub fn new_v1( |
110 on_disk: &'on_disk [u8], |
108 on_disk: &'on_disk [u8], |
111 ) -> Result<(Self, Option<DirstateParents>), DirstateError> { |
109 ) -> Result<(Self, Option<DirstateParents>), DirstateError> { |
112 let mut map = Self { |
110 let mut map = Self::empty(on_disk); |
113 on_disk, |
|
114 root: ChildNodes::default(), |
|
115 nodes_with_entry_count: 0, |
|
116 nodes_with_copy_source_count: 0, |
|
117 }; |
|
118 if map.on_disk.is_empty() { |
111 if map.on_disk.is_empty() { |
119 return Ok((map, None)); |
112 return Ok((map, None)); |
120 } |
113 } |
121 |
114 |
122 let parents = parse_dirstate_entries( |
115 let parents = parse_dirstate_entries( |
563 fn pack_v2( |
556 fn pack_v2( |
564 &mut self, |
557 &mut self, |
565 parents: DirstateParents, |
558 parents: DirstateParents, |
566 now: Timestamp, |
559 now: Timestamp, |
567 ) -> Result<Vec<u8>, DirstateError> { |
560 ) -> Result<Vec<u8>, DirstateError> { |
568 // Inefficient but temporary |
561 on_disk::write(self, parents, now) |
569 let mut v2 = V2_FORMAT_MARKER.to_vec(); |
|
570 v2.append(&mut self.pack_v1(parents, now)?); |
|
571 Ok(v2) |
|
572 } |
562 } |
573 |
563 |
574 fn set_all_dirs(&mut self) -> Result<(), DirstateMapError> { |
564 fn set_all_dirs(&mut self) -> Result<(), DirstateMapError> { |
575 // Do nothing, this `DirstateMap` does not a separate `all_dirs` that |
565 // Do nothing, this `DirstateMap` does not a separate `all_dirs` that |
576 // needs to be recomputed |
566 // needs to be recomputed |
593 { |
583 { |
594 super::status::status(self, matcher, root_dir, ignore_files, options) |
584 super::status::status(self, matcher, root_dir, ignore_files, options) |
595 } |
585 } |
596 |
586 |
597 fn copy_map_len(&self) -> usize { |
587 fn copy_map_len(&self) -> usize { |
598 self.nodes_with_copy_source_count |
588 self.nodes_with_copy_source_count as usize |
599 } |
589 } |
600 |
590 |
601 fn copy_map_iter(&self) -> CopyMapIter<'_> { |
591 fn copy_map_iter(&self) -> CopyMapIter<'_> { |
602 Box::new(self.iter_nodes().filter_map(|(path, node)| { |
592 Box::new(self.iter_nodes().filter_map(|(path, node)| { |
603 node.copy_source |
593 node.copy_source |
644 } |
634 } |
645 node.copy_source.replace(value.into()).map(Cow::into_owned) |
635 node.copy_source.replace(value.into()).map(Cow::into_owned) |
646 } |
636 } |
647 |
637 |
648 fn len(&self) -> usize { |
638 fn len(&self) -> usize { |
649 self.nodes_with_entry_count |
639 self.nodes_with_entry_count as usize |
650 } |
640 } |
651 |
641 |
652 fn contains_key(&self, key: &HgPath) -> bool { |
642 fn contains_key(&self, key: &HgPath) -> bool { |
653 self.get(key).is_some() |
643 self.get(key).is_some() |
654 } |
644 } |