##// END OF EJS Templates
rust-dirstatemap: add `each_ancestor` argument to `get_node_mut`...
Raphaël Gomès -
r50022:fcf6f943 default
parent child Browse files
Show More
@@ -1,1861 +1,1870 b''
1 use bytes_cast::BytesCast;
1 use bytes_cast::BytesCast;
2 use micro_timer::timed;
2 use micro_timer::timed;
3 use std::borrow::Cow;
3 use std::borrow::Cow;
4 use std::path::PathBuf;
4 use std::path::PathBuf;
5
5
6 use super::on_disk;
6 use super::on_disk;
7 use super::on_disk::DirstateV2ParseError;
7 use super::on_disk::DirstateV2ParseError;
8 use super::owning::OwningDirstateMap;
8 use super::owning::OwningDirstateMap;
9 use super::path_with_basename::WithBasename;
9 use super::path_with_basename::WithBasename;
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::CopyMapIter;
13 use crate::dirstate::CopyMapIter;
14 use crate::dirstate::DirstateV2Data;
14 use crate::dirstate::DirstateV2Data;
15 use crate::dirstate::ParentFileData;
15 use crate::dirstate::ParentFileData;
16 use crate::dirstate::StateMapIter;
16 use crate::dirstate::StateMapIter;
17 use crate::dirstate::TruncatedTimestamp;
17 use crate::dirstate::TruncatedTimestamp;
18 use crate::matchers::Matcher;
18 use crate::matchers::Matcher;
19 use crate::utils::hg_path::{HgPath, HgPathBuf};
19 use crate::utils::hg_path::{HgPath, HgPathBuf};
20 use crate::DirstateEntry;
20 use crate::DirstateEntry;
21 use crate::DirstateError;
21 use crate::DirstateError;
22 use crate::DirstateMapError;
22 use crate::DirstateMapError;
23 use crate::DirstateParents;
23 use crate::DirstateParents;
24 use crate::DirstateStatus;
24 use crate::DirstateStatus;
25 use crate::EntryState;
25 use crate::EntryState;
26 use crate::FastHashbrownMap as FastHashMap;
26 use crate::FastHashbrownMap as FastHashMap;
27 use crate::PatternFileWarning;
27 use crate::PatternFileWarning;
28 use crate::StatusError;
28 use crate::StatusError;
29 use crate::StatusOptions;
29 use crate::StatusOptions;
30
30
31 /// Append to an existing data file if the amount of unreachable data (not used
31 /// Append to an existing data file if the amount of unreachable data (not used
32 /// anymore) is less than this fraction of the total amount of existing data.
32 /// anymore) is less than this fraction of the total amount of existing data.
33 const ACCEPTABLE_UNREACHABLE_BYTES_RATIO: f32 = 0.5;
33 const ACCEPTABLE_UNREACHABLE_BYTES_RATIO: f32 = 0.5;
34
34
35 #[derive(Debug)]
35 #[derive(Debug)]
36 pub struct DirstateMap<'on_disk> {
36 pub struct DirstateMap<'on_disk> {
37 /// Contents of the `.hg/dirstate` file
37 /// Contents of the `.hg/dirstate` file
38 pub(super) on_disk: &'on_disk [u8],
38 pub(super) on_disk: &'on_disk [u8],
39
39
40 pub(super) root: ChildNodes<'on_disk>,
40 pub(super) root: ChildNodes<'on_disk>,
41
41
42 /// Number of nodes anywhere in the tree that have `.entry.is_some()`.
42 /// Number of nodes anywhere in the tree that have `.entry.is_some()`.
43 pub(super) nodes_with_entry_count: u32,
43 pub(super) nodes_with_entry_count: u32,
44
44
45 /// Number of nodes anywhere in the tree that have
45 /// Number of nodes anywhere in the tree that have
46 /// `.copy_source.is_some()`.
46 /// `.copy_source.is_some()`.
47 pub(super) nodes_with_copy_source_count: u32,
47 pub(super) nodes_with_copy_source_count: u32,
48
48
49 /// See on_disk::Header
49 /// See on_disk::Header
50 pub(super) ignore_patterns_hash: on_disk::IgnorePatternsHash,
50 pub(super) ignore_patterns_hash: on_disk::IgnorePatternsHash,
51
51
52 /// How many bytes of `on_disk` are not used anymore
52 /// How many bytes of `on_disk` are not used anymore
53 pub(super) unreachable_bytes: u32,
53 pub(super) unreachable_bytes: u32,
54 }
54 }
55
55
56 /// Using a plain `HgPathBuf` of the full path from the repository root as a
56 /// Using a plain `HgPathBuf` of the full path from the repository root as a
57 /// map key would also work: all paths in a given map have the same parent
57 /// map key would also work: all paths in a given map have the same parent
58 /// path, so comparing full paths gives the same result as comparing base
58 /// path, so comparing full paths gives the same result as comparing base
59 /// names. However `HashMap` would waste time always re-hashing the same
59 /// names. However `HashMap` would waste time always re-hashing the same
60 /// string prefix.
60 /// string prefix.
61 pub(super) type NodeKey<'on_disk> = WithBasename<Cow<'on_disk, HgPath>>;
61 pub(super) type NodeKey<'on_disk> = WithBasename<Cow<'on_disk, HgPath>>;
62
62
63 /// Similar to `&'tree Cow<'on_disk, HgPath>`, but can also be returned
63 /// Similar to `&'tree Cow<'on_disk, HgPath>`, but can also be returned
64 /// for on-disk nodes that don’t actually have a `Cow` to borrow.
64 /// for on-disk nodes that don’t actually have a `Cow` to borrow.
65 #[derive(Debug)]
65 #[derive(Debug)]
66 pub(super) enum BorrowedPath<'tree, 'on_disk> {
66 pub(super) enum BorrowedPath<'tree, 'on_disk> {
67 InMemory(&'tree HgPathBuf),
67 InMemory(&'tree HgPathBuf),
68 OnDisk(&'on_disk HgPath),
68 OnDisk(&'on_disk HgPath),
69 }
69 }
70
70
71 #[derive(Debug)]
71 #[derive(Debug)]
72 pub(super) enum ChildNodes<'on_disk> {
72 pub(super) enum ChildNodes<'on_disk> {
73 InMemory(FastHashMap<NodeKey<'on_disk>, Node<'on_disk>>),
73 InMemory(FastHashMap<NodeKey<'on_disk>, Node<'on_disk>>),
74 OnDisk(&'on_disk [on_disk::Node]),
74 OnDisk(&'on_disk [on_disk::Node]),
75 }
75 }
76
76
77 #[derive(Debug)]
77 #[derive(Debug)]
78 pub(super) enum ChildNodesRef<'tree, 'on_disk> {
78 pub(super) enum ChildNodesRef<'tree, 'on_disk> {
79 InMemory(&'tree FastHashMap<NodeKey<'on_disk>, Node<'on_disk>>),
79 InMemory(&'tree FastHashMap<NodeKey<'on_disk>, Node<'on_disk>>),
80 OnDisk(&'on_disk [on_disk::Node]),
80 OnDisk(&'on_disk [on_disk::Node]),
81 }
81 }
82
82
83 #[derive(Debug)]
83 #[derive(Debug)]
84 pub(super) enum NodeRef<'tree, 'on_disk> {
84 pub(super) enum NodeRef<'tree, 'on_disk> {
85 InMemory(&'tree NodeKey<'on_disk>, &'tree Node<'on_disk>),
85 InMemory(&'tree NodeKey<'on_disk>, &'tree Node<'on_disk>),
86 OnDisk(&'on_disk on_disk::Node),
86 OnDisk(&'on_disk on_disk::Node),
87 }
87 }
88
88
89 impl<'tree, 'on_disk> BorrowedPath<'tree, 'on_disk> {
89 impl<'tree, 'on_disk> BorrowedPath<'tree, 'on_disk> {
90 pub fn detach_from_tree(&self) -> Cow<'on_disk, HgPath> {
90 pub fn detach_from_tree(&self) -> Cow<'on_disk, HgPath> {
91 match *self {
91 match *self {
92 BorrowedPath::InMemory(in_memory) => Cow::Owned(in_memory.clone()),
92 BorrowedPath::InMemory(in_memory) => Cow::Owned(in_memory.clone()),
93 BorrowedPath::OnDisk(on_disk) => Cow::Borrowed(on_disk),
93 BorrowedPath::OnDisk(on_disk) => Cow::Borrowed(on_disk),
94 }
94 }
95 }
95 }
96 }
96 }
97
97
98 impl<'tree, 'on_disk> std::ops::Deref for BorrowedPath<'tree, 'on_disk> {
98 impl<'tree, 'on_disk> std::ops::Deref for BorrowedPath<'tree, 'on_disk> {
99 type Target = HgPath;
99 type Target = HgPath;
100
100
101 fn deref(&self) -> &HgPath {
101 fn deref(&self) -> &HgPath {
102 match *self {
102 match *self {
103 BorrowedPath::InMemory(in_memory) => in_memory,
103 BorrowedPath::InMemory(in_memory) => in_memory,
104 BorrowedPath::OnDisk(on_disk) => on_disk,
104 BorrowedPath::OnDisk(on_disk) => on_disk,
105 }
105 }
106 }
106 }
107 }
107 }
108
108
109 impl Default for ChildNodes<'_> {
109 impl Default for ChildNodes<'_> {
110 fn default() -> Self {
110 fn default() -> Self {
111 ChildNodes::InMemory(Default::default())
111 ChildNodes::InMemory(Default::default())
112 }
112 }
113 }
113 }
114
114
115 impl<'on_disk> ChildNodes<'on_disk> {
115 impl<'on_disk> ChildNodes<'on_disk> {
116 pub(super) fn as_ref<'tree>(
116 pub(super) fn as_ref<'tree>(
117 &'tree self,
117 &'tree self,
118 ) -> ChildNodesRef<'tree, 'on_disk> {
118 ) -> ChildNodesRef<'tree, 'on_disk> {
119 match self {
119 match self {
120 ChildNodes::InMemory(nodes) => ChildNodesRef::InMemory(nodes),
120 ChildNodes::InMemory(nodes) => ChildNodesRef::InMemory(nodes),
121 ChildNodes::OnDisk(nodes) => ChildNodesRef::OnDisk(nodes),
121 ChildNodes::OnDisk(nodes) => ChildNodesRef::OnDisk(nodes),
122 }
122 }
123 }
123 }
124
124
125 pub(super) fn is_empty(&self) -> bool {
125 pub(super) fn is_empty(&self) -> bool {
126 match self {
126 match self {
127 ChildNodes::InMemory(nodes) => nodes.is_empty(),
127 ChildNodes::InMemory(nodes) => nodes.is_empty(),
128 ChildNodes::OnDisk(nodes) => nodes.is_empty(),
128 ChildNodes::OnDisk(nodes) => nodes.is_empty(),
129 }
129 }
130 }
130 }
131
131
132 fn make_mut(
132 fn make_mut(
133 &mut self,
133 &mut self,
134 on_disk: &'on_disk [u8],
134 on_disk: &'on_disk [u8],
135 unreachable_bytes: &mut u32,
135 unreachable_bytes: &mut u32,
136 ) -> Result<
136 ) -> Result<
137 &mut FastHashMap<NodeKey<'on_disk>, Node<'on_disk>>,
137 &mut FastHashMap<NodeKey<'on_disk>, Node<'on_disk>>,
138 DirstateV2ParseError,
138 DirstateV2ParseError,
139 > {
139 > {
140 match self {
140 match self {
141 ChildNodes::InMemory(nodes) => Ok(nodes),
141 ChildNodes::InMemory(nodes) => Ok(nodes),
142 ChildNodes::OnDisk(nodes) => {
142 ChildNodes::OnDisk(nodes) => {
143 *unreachable_bytes +=
143 *unreachable_bytes +=
144 std::mem::size_of_val::<[on_disk::Node]>(nodes) as u32;
144 std::mem::size_of_val::<[on_disk::Node]>(nodes) as u32;
145 let nodes = nodes
145 let nodes = nodes
146 .iter()
146 .iter()
147 .map(|node| {
147 .map(|node| {
148 Ok((
148 Ok((
149 node.path(on_disk)?,
149 node.path(on_disk)?,
150 node.to_in_memory_node(on_disk)?,
150 node.to_in_memory_node(on_disk)?,
151 ))
151 ))
152 })
152 })
153 .collect::<Result<_, _>>()?;
153 .collect::<Result<_, _>>()?;
154 *self = ChildNodes::InMemory(nodes);
154 *self = ChildNodes::InMemory(nodes);
155 match self {
155 match self {
156 ChildNodes::InMemory(nodes) => Ok(nodes),
156 ChildNodes::InMemory(nodes) => Ok(nodes),
157 ChildNodes::OnDisk(_) => unreachable!(),
157 ChildNodes::OnDisk(_) => unreachable!(),
158 }
158 }
159 }
159 }
160 }
160 }
161 }
161 }
162 }
162 }
163
163
164 impl<'tree, 'on_disk> ChildNodesRef<'tree, 'on_disk> {
164 impl<'tree, 'on_disk> ChildNodesRef<'tree, 'on_disk> {
165 pub(super) fn get(
165 pub(super) fn get(
166 &self,
166 &self,
167 base_name: &HgPath,
167 base_name: &HgPath,
168 on_disk: &'on_disk [u8],
168 on_disk: &'on_disk [u8],
169 ) -> Result<Option<NodeRef<'tree, 'on_disk>>, DirstateV2ParseError> {
169 ) -> Result<Option<NodeRef<'tree, 'on_disk>>, DirstateV2ParseError> {
170 match self {
170 match self {
171 ChildNodesRef::InMemory(nodes) => Ok(nodes
171 ChildNodesRef::InMemory(nodes) => Ok(nodes
172 .get_key_value(base_name)
172 .get_key_value(base_name)
173 .map(|(k, v)| NodeRef::InMemory(k, v))),
173 .map(|(k, v)| NodeRef::InMemory(k, v))),
174 ChildNodesRef::OnDisk(nodes) => {
174 ChildNodesRef::OnDisk(nodes) => {
175 let mut parse_result = Ok(());
175 let mut parse_result = Ok(());
176 let search_result = nodes.binary_search_by(|node| {
176 let search_result = nodes.binary_search_by(|node| {
177 match node.base_name(on_disk) {
177 match node.base_name(on_disk) {
178 Ok(node_base_name) => node_base_name.cmp(base_name),
178 Ok(node_base_name) => node_base_name.cmp(base_name),
179 Err(e) => {
179 Err(e) => {
180 parse_result = Err(e);
180 parse_result = Err(e);
181 // Dummy comparison result, `search_result` won’t
181 // Dummy comparison result, `search_result` won’t
182 // be used since `parse_result` is an error
182 // be used since `parse_result` is an error
183 std::cmp::Ordering::Equal
183 std::cmp::Ordering::Equal
184 }
184 }
185 }
185 }
186 });
186 });
187 parse_result.map(|()| {
187 parse_result.map(|()| {
188 search_result.ok().map(|i| NodeRef::OnDisk(&nodes[i]))
188 search_result.ok().map(|i| NodeRef::OnDisk(&nodes[i]))
189 })
189 })
190 }
190 }
191 }
191 }
192 }
192 }
193
193
194 /// Iterate in undefined order
194 /// Iterate in undefined order
195 pub(super) fn iter(
195 pub(super) fn iter(
196 &self,
196 &self,
197 ) -> impl Iterator<Item = NodeRef<'tree, 'on_disk>> {
197 ) -> impl Iterator<Item = NodeRef<'tree, 'on_disk>> {
198 match self {
198 match self {
199 ChildNodesRef::InMemory(nodes) => itertools::Either::Left(
199 ChildNodesRef::InMemory(nodes) => itertools::Either::Left(
200 nodes.iter().map(|(k, v)| NodeRef::InMemory(k, v)),
200 nodes.iter().map(|(k, v)| NodeRef::InMemory(k, v)),
201 ),
201 ),
202 ChildNodesRef::OnDisk(nodes) => {
202 ChildNodesRef::OnDisk(nodes) => {
203 itertools::Either::Right(nodes.iter().map(NodeRef::OnDisk))
203 itertools::Either::Right(nodes.iter().map(NodeRef::OnDisk))
204 }
204 }
205 }
205 }
206 }
206 }
207
207
208 /// Iterate in parallel in undefined order
208 /// Iterate in parallel in undefined order
209 pub(super) fn par_iter(
209 pub(super) fn par_iter(
210 &self,
210 &self,
211 ) -> impl rayon::iter::ParallelIterator<Item = NodeRef<'tree, 'on_disk>>
211 ) -> impl rayon::iter::ParallelIterator<Item = NodeRef<'tree, 'on_disk>>
212 {
212 {
213 use rayon::prelude::*;
213 use rayon::prelude::*;
214 match self {
214 match self {
215 ChildNodesRef::InMemory(nodes) => rayon::iter::Either::Left(
215 ChildNodesRef::InMemory(nodes) => rayon::iter::Either::Left(
216 nodes.par_iter().map(|(k, v)| NodeRef::InMemory(k, v)),
216 nodes.par_iter().map(|(k, v)| NodeRef::InMemory(k, v)),
217 ),
217 ),
218 ChildNodesRef::OnDisk(nodes) => rayon::iter::Either::Right(
218 ChildNodesRef::OnDisk(nodes) => rayon::iter::Either::Right(
219 nodes.par_iter().map(NodeRef::OnDisk),
219 nodes.par_iter().map(NodeRef::OnDisk),
220 ),
220 ),
221 }
221 }
222 }
222 }
223
223
224 pub(super) fn sorted(&self) -> Vec<NodeRef<'tree, 'on_disk>> {
224 pub(super) fn sorted(&self) -> Vec<NodeRef<'tree, 'on_disk>> {
225 match self {
225 match self {
226 ChildNodesRef::InMemory(nodes) => {
226 ChildNodesRef::InMemory(nodes) => {
227 let mut vec: Vec<_> = nodes
227 let mut vec: Vec<_> = nodes
228 .iter()
228 .iter()
229 .map(|(k, v)| NodeRef::InMemory(k, v))
229 .map(|(k, v)| NodeRef::InMemory(k, v))
230 .collect();
230 .collect();
231 fn sort_key<'a>(node: &'a NodeRef) -> &'a HgPath {
231 fn sort_key<'a>(node: &'a NodeRef) -> &'a HgPath {
232 match node {
232 match node {
233 NodeRef::InMemory(path, _node) => path.base_name(),
233 NodeRef::InMemory(path, _node) => path.base_name(),
234 NodeRef::OnDisk(_) => unreachable!(),
234 NodeRef::OnDisk(_) => unreachable!(),
235 }
235 }
236 }
236 }
237 // `sort_unstable_by_key` doesn’t allow keys borrowing from the
237 // `sort_unstable_by_key` doesn’t allow keys borrowing from the
238 // value: https://github.com/rust-lang/rust/issues/34162
238 // value: https://github.com/rust-lang/rust/issues/34162
239 vec.sort_unstable_by(|a, b| sort_key(a).cmp(sort_key(b)));
239 vec.sort_unstable_by(|a, b| sort_key(a).cmp(sort_key(b)));
240 vec
240 vec
241 }
241 }
242 ChildNodesRef::OnDisk(nodes) => {
242 ChildNodesRef::OnDisk(nodes) => {
243 // Nodes on disk are already sorted
243 // Nodes on disk are already sorted
244 nodes.iter().map(NodeRef::OnDisk).collect()
244 nodes.iter().map(NodeRef::OnDisk).collect()
245 }
245 }
246 }
246 }
247 }
247 }
248 }
248 }
249
249
250 impl<'tree, 'on_disk> NodeRef<'tree, 'on_disk> {
250 impl<'tree, 'on_disk> NodeRef<'tree, 'on_disk> {
251 pub(super) fn full_path(
251 pub(super) fn full_path(
252 &self,
252 &self,
253 on_disk: &'on_disk [u8],
253 on_disk: &'on_disk [u8],
254 ) -> Result<&'tree HgPath, DirstateV2ParseError> {
254 ) -> Result<&'tree HgPath, DirstateV2ParseError> {
255 match self {
255 match self {
256 NodeRef::InMemory(path, _node) => Ok(path.full_path()),
256 NodeRef::InMemory(path, _node) => Ok(path.full_path()),
257 NodeRef::OnDisk(node) => node.full_path(on_disk),
257 NodeRef::OnDisk(node) => node.full_path(on_disk),
258 }
258 }
259 }
259 }
260
260
261 /// Returns a `BorrowedPath`, which can be turned into a `Cow<'on_disk,
261 /// Returns a `BorrowedPath`, which can be turned into a `Cow<'on_disk,
262 /// HgPath>` detached from `'tree`
262 /// HgPath>` detached from `'tree`
263 pub(super) fn full_path_borrowed(
263 pub(super) fn full_path_borrowed(
264 &self,
264 &self,
265 on_disk: &'on_disk [u8],
265 on_disk: &'on_disk [u8],
266 ) -> Result<BorrowedPath<'tree, 'on_disk>, DirstateV2ParseError> {
266 ) -> Result<BorrowedPath<'tree, 'on_disk>, DirstateV2ParseError> {
267 match self {
267 match self {
268 NodeRef::InMemory(path, _node) => match path.full_path() {
268 NodeRef::InMemory(path, _node) => match path.full_path() {
269 Cow::Borrowed(on_disk) => Ok(BorrowedPath::OnDisk(on_disk)),
269 Cow::Borrowed(on_disk) => Ok(BorrowedPath::OnDisk(on_disk)),
270 Cow::Owned(in_memory) => Ok(BorrowedPath::InMemory(in_memory)),
270 Cow::Owned(in_memory) => Ok(BorrowedPath::InMemory(in_memory)),
271 },
271 },
272 NodeRef::OnDisk(node) => {
272 NodeRef::OnDisk(node) => {
273 Ok(BorrowedPath::OnDisk(node.full_path(on_disk)?))
273 Ok(BorrowedPath::OnDisk(node.full_path(on_disk)?))
274 }
274 }
275 }
275 }
276 }
276 }
277
277
278 pub(super) fn base_name(
278 pub(super) fn base_name(
279 &self,
279 &self,
280 on_disk: &'on_disk [u8],
280 on_disk: &'on_disk [u8],
281 ) -> Result<&'tree HgPath, DirstateV2ParseError> {
281 ) -> Result<&'tree HgPath, DirstateV2ParseError> {
282 match self {
282 match self {
283 NodeRef::InMemory(path, _node) => Ok(path.base_name()),
283 NodeRef::InMemory(path, _node) => Ok(path.base_name()),
284 NodeRef::OnDisk(node) => node.base_name(on_disk),
284 NodeRef::OnDisk(node) => node.base_name(on_disk),
285 }
285 }
286 }
286 }
287
287
288 pub(super) fn children(
288 pub(super) fn children(
289 &self,
289 &self,
290 on_disk: &'on_disk [u8],
290 on_disk: &'on_disk [u8],
291 ) -> Result<ChildNodesRef<'tree, 'on_disk>, DirstateV2ParseError> {
291 ) -> Result<ChildNodesRef<'tree, 'on_disk>, DirstateV2ParseError> {
292 match self {
292 match self {
293 NodeRef::InMemory(_path, node) => Ok(node.children.as_ref()),
293 NodeRef::InMemory(_path, node) => Ok(node.children.as_ref()),
294 NodeRef::OnDisk(node) => {
294 NodeRef::OnDisk(node) => {
295 Ok(ChildNodesRef::OnDisk(node.children(on_disk)?))
295 Ok(ChildNodesRef::OnDisk(node.children(on_disk)?))
296 }
296 }
297 }
297 }
298 }
298 }
299
299
300 pub(super) fn has_copy_source(&self) -> bool {
300 pub(super) fn has_copy_source(&self) -> bool {
301 match self {
301 match self {
302 NodeRef::InMemory(_path, node) => node.copy_source.is_some(),
302 NodeRef::InMemory(_path, node) => node.copy_source.is_some(),
303 NodeRef::OnDisk(node) => node.has_copy_source(),
303 NodeRef::OnDisk(node) => node.has_copy_source(),
304 }
304 }
305 }
305 }
306
306
307 pub(super) fn copy_source(
307 pub(super) fn copy_source(
308 &self,
308 &self,
309 on_disk: &'on_disk [u8],
309 on_disk: &'on_disk [u8],
310 ) -> Result<Option<&'tree HgPath>, DirstateV2ParseError> {
310 ) -> Result<Option<&'tree HgPath>, DirstateV2ParseError> {
311 match self {
311 match self {
312 NodeRef::InMemory(_path, node) => {
312 NodeRef::InMemory(_path, node) => {
313 Ok(node.copy_source.as_ref().map(|s| &**s))
313 Ok(node.copy_source.as_ref().map(|s| &**s))
314 }
314 }
315 NodeRef::OnDisk(node) => node.copy_source(on_disk),
315 NodeRef::OnDisk(node) => node.copy_source(on_disk),
316 }
316 }
317 }
317 }
318 /// Returns a `BorrowedPath`, which can be turned into a `Cow<'on_disk,
318 /// Returns a `BorrowedPath`, which can be turned into a `Cow<'on_disk,
319 /// HgPath>` detached from `'tree`
319 /// HgPath>` detached from `'tree`
320 pub(super) fn copy_source_borrowed(
320 pub(super) fn copy_source_borrowed(
321 &self,
321 &self,
322 on_disk: &'on_disk [u8],
322 on_disk: &'on_disk [u8],
323 ) -> Result<Option<BorrowedPath<'tree, 'on_disk>>, DirstateV2ParseError>
323 ) -> Result<Option<BorrowedPath<'tree, 'on_disk>>, DirstateV2ParseError>
324 {
324 {
325 Ok(match self {
325 Ok(match self {
326 NodeRef::InMemory(_path, node) => {
326 NodeRef::InMemory(_path, node) => {
327 node.copy_source.as_ref().map(|source| match source {
327 node.copy_source.as_ref().map(|source| match source {
328 Cow::Borrowed(on_disk) => BorrowedPath::OnDisk(on_disk),
328 Cow::Borrowed(on_disk) => BorrowedPath::OnDisk(on_disk),
329 Cow::Owned(in_memory) => BorrowedPath::InMemory(in_memory),
329 Cow::Owned(in_memory) => BorrowedPath::InMemory(in_memory),
330 })
330 })
331 }
331 }
332 NodeRef::OnDisk(node) => node
332 NodeRef::OnDisk(node) => node
333 .copy_source(on_disk)?
333 .copy_source(on_disk)?
334 .map(|source| BorrowedPath::OnDisk(source)),
334 .map(|source| BorrowedPath::OnDisk(source)),
335 })
335 })
336 }
336 }
337
337
338 pub(super) fn entry(
338 pub(super) fn entry(
339 &self,
339 &self,
340 ) -> Result<Option<DirstateEntry>, DirstateV2ParseError> {
340 ) -> Result<Option<DirstateEntry>, DirstateV2ParseError> {
341 match self {
341 match self {
342 NodeRef::InMemory(_path, node) => {
342 NodeRef::InMemory(_path, node) => {
343 Ok(node.data.as_entry().copied())
343 Ok(node.data.as_entry().copied())
344 }
344 }
345 NodeRef::OnDisk(node) => node.entry(),
345 NodeRef::OnDisk(node) => node.entry(),
346 }
346 }
347 }
347 }
348
348
349 pub(super) fn state(
349 pub(super) fn state(
350 &self,
350 &self,
351 ) -> Result<Option<EntryState>, DirstateV2ParseError> {
351 ) -> Result<Option<EntryState>, DirstateV2ParseError> {
352 Ok(self.entry()?.and_then(|e| {
352 Ok(self.entry()?.and_then(|e| {
353 if e.any_tracked() {
353 if e.any_tracked() {
354 Some(e.state())
354 Some(e.state())
355 } else {
355 } else {
356 None
356 None
357 }
357 }
358 }))
358 }))
359 }
359 }
360
360
361 pub(super) fn cached_directory_mtime(
361 pub(super) fn cached_directory_mtime(
362 &self,
362 &self,
363 ) -> Result<Option<TruncatedTimestamp>, DirstateV2ParseError> {
363 ) -> Result<Option<TruncatedTimestamp>, DirstateV2ParseError> {
364 match self {
364 match self {
365 NodeRef::InMemory(_path, node) => Ok(match node.data {
365 NodeRef::InMemory(_path, node) => Ok(match node.data {
366 NodeData::CachedDirectory { mtime } => Some(mtime),
366 NodeData::CachedDirectory { mtime } => Some(mtime),
367 _ => None,
367 _ => None,
368 }),
368 }),
369 NodeRef::OnDisk(node) => node.cached_directory_mtime(),
369 NodeRef::OnDisk(node) => node.cached_directory_mtime(),
370 }
370 }
371 }
371 }
372
372
373 pub(super) fn descendants_with_entry_count(&self) -> u32 {
373 pub(super) fn descendants_with_entry_count(&self) -> u32 {
374 match self {
374 match self {
375 NodeRef::InMemory(_path, node) => {
375 NodeRef::InMemory(_path, node) => {
376 node.descendants_with_entry_count
376 node.descendants_with_entry_count
377 }
377 }
378 NodeRef::OnDisk(node) => node.descendants_with_entry_count.get(),
378 NodeRef::OnDisk(node) => node.descendants_with_entry_count.get(),
379 }
379 }
380 }
380 }
381
381
382 pub(super) fn tracked_descendants_count(&self) -> u32 {
382 pub(super) fn tracked_descendants_count(&self) -> u32 {
383 match self {
383 match self {
384 NodeRef::InMemory(_path, node) => node.tracked_descendants_count,
384 NodeRef::InMemory(_path, node) => node.tracked_descendants_count,
385 NodeRef::OnDisk(node) => node.tracked_descendants_count.get(),
385 NodeRef::OnDisk(node) => node.tracked_descendants_count.get(),
386 }
386 }
387 }
387 }
388 }
388 }
389
389
390 /// Represents a file or a directory
390 /// Represents a file or a directory
391 #[derive(Default, Debug)]
391 #[derive(Default, Debug)]
392 pub(super) struct Node<'on_disk> {
392 pub(super) struct Node<'on_disk> {
393 pub(super) data: NodeData,
393 pub(super) data: NodeData,
394
394
395 pub(super) copy_source: Option<Cow<'on_disk, HgPath>>,
395 pub(super) copy_source: Option<Cow<'on_disk, HgPath>>,
396
396
397 pub(super) children: ChildNodes<'on_disk>,
397 pub(super) children: ChildNodes<'on_disk>,
398
398
399 /// How many (non-inclusive) descendants of this node have an entry.
399 /// How many (non-inclusive) descendants of this node have an entry.
400 pub(super) descendants_with_entry_count: u32,
400 pub(super) descendants_with_entry_count: u32,
401
401
402 /// How many (non-inclusive) descendants of this node have an entry whose
402 /// How many (non-inclusive) descendants of this node have an entry whose
403 /// state is "tracked".
403 /// state is "tracked".
404 pub(super) tracked_descendants_count: u32,
404 pub(super) tracked_descendants_count: u32,
405 }
405 }
406
406
407 #[derive(Debug)]
407 #[derive(Debug)]
408 pub(super) enum NodeData {
408 pub(super) enum NodeData {
409 Entry(DirstateEntry),
409 Entry(DirstateEntry),
410 CachedDirectory { mtime: TruncatedTimestamp },
410 CachedDirectory { mtime: TruncatedTimestamp },
411 None,
411 None,
412 }
412 }
413
413
414 impl Default for NodeData {
414 impl Default for NodeData {
415 fn default() -> Self {
415 fn default() -> Self {
416 NodeData::None
416 NodeData::None
417 }
417 }
418 }
418 }
419
419
420 impl NodeData {
420 impl NodeData {
421 fn has_entry(&self) -> bool {
421 fn has_entry(&self) -> bool {
422 match self {
422 match self {
423 NodeData::Entry(_) => true,
423 NodeData::Entry(_) => true,
424 _ => false,
424 _ => false,
425 }
425 }
426 }
426 }
427
427
428 fn as_entry(&self) -> Option<&DirstateEntry> {
428 fn as_entry(&self) -> Option<&DirstateEntry> {
429 match self {
429 match self {
430 NodeData::Entry(entry) => Some(entry),
430 NodeData::Entry(entry) => Some(entry),
431 _ => None,
431 _ => None,
432 }
432 }
433 }
433 }
434
434
435 fn as_entry_mut(&mut self) -> Option<&mut DirstateEntry> {
435 fn as_entry_mut(&mut self) -> Option<&mut DirstateEntry> {
436 match self {
436 match self {
437 NodeData::Entry(entry) => Some(entry),
437 NodeData::Entry(entry) => Some(entry),
438 _ => None,
438 _ => None,
439 }
439 }
440 }
440 }
441 }
441 }
442
442
443 impl<'on_disk> DirstateMap<'on_disk> {
443 impl<'on_disk> DirstateMap<'on_disk> {
444 pub(super) fn empty(on_disk: &'on_disk [u8]) -> Self {
444 pub(super) fn empty(on_disk: &'on_disk [u8]) -> Self {
445 Self {
445 Self {
446 on_disk,
446 on_disk,
447 root: ChildNodes::default(),
447 root: ChildNodes::default(),
448 nodes_with_entry_count: 0,
448 nodes_with_entry_count: 0,
449 nodes_with_copy_source_count: 0,
449 nodes_with_copy_source_count: 0,
450 ignore_patterns_hash: [0; on_disk::IGNORE_PATTERNS_HASH_LEN],
450 ignore_patterns_hash: [0; on_disk::IGNORE_PATTERNS_HASH_LEN],
451 unreachable_bytes: 0,
451 unreachable_bytes: 0,
452 }
452 }
453 }
453 }
454
454
455 #[timed]
455 #[timed]
456 pub fn new_v2(
456 pub fn new_v2(
457 on_disk: &'on_disk [u8],
457 on_disk: &'on_disk [u8],
458 data_size: usize,
458 data_size: usize,
459 metadata: &[u8],
459 metadata: &[u8],
460 ) -> Result<Self, DirstateError> {
460 ) -> Result<Self, DirstateError> {
461 if let Some(data) = on_disk.get(..data_size) {
461 if let Some(data) = on_disk.get(..data_size) {
462 Ok(on_disk::read(data, metadata)?)
462 Ok(on_disk::read(data, metadata)?)
463 } else {
463 } else {
464 Err(DirstateV2ParseError.into())
464 Err(DirstateV2ParseError.into())
465 }
465 }
466 }
466 }
467
467
468 #[timed]
468 #[timed]
469 pub fn new_v1(
469 pub fn new_v1(
470 on_disk: &'on_disk [u8],
470 on_disk: &'on_disk [u8],
471 ) -> Result<(Self, Option<DirstateParents>), DirstateError> {
471 ) -> Result<(Self, Option<DirstateParents>), DirstateError> {
472 let mut map = Self::empty(on_disk);
472 let mut map = Self::empty(on_disk);
473 if map.on_disk.is_empty() {
473 if map.on_disk.is_empty() {
474 return Ok((map, None));
474 return Ok((map, None));
475 }
475 }
476
476
477 let parents = parse_dirstate_entries(
477 let parents = parse_dirstate_entries(
478 map.on_disk,
478 map.on_disk,
479 |path, entry, copy_source| {
479 |path, entry, copy_source| {
480 let tracked = entry.state().is_tracked();
480 let tracked = entry.state().is_tracked();
481 let node = Self::get_or_insert_node_inner(
481 let node = Self::get_or_insert_node_inner(
482 map.on_disk,
482 map.on_disk,
483 &mut map.unreachable_bytes,
483 &mut map.unreachable_bytes,
484 &mut map.root,
484 &mut map.root,
485 path,
485 path,
486 WithBasename::to_cow_borrowed,
486 WithBasename::to_cow_borrowed,
487 |ancestor| {
487 |ancestor| {
488 if tracked {
488 if tracked {
489 ancestor.tracked_descendants_count += 1
489 ancestor.tracked_descendants_count += 1
490 }
490 }
491 ancestor.descendants_with_entry_count += 1
491 ancestor.descendants_with_entry_count += 1
492 },
492 },
493 )?;
493 )?;
494 assert!(
494 assert!(
495 !node.data.has_entry(),
495 !node.data.has_entry(),
496 "duplicate dirstate entry in read"
496 "duplicate dirstate entry in read"
497 );
497 );
498 assert!(
498 assert!(
499 node.copy_source.is_none(),
499 node.copy_source.is_none(),
500 "duplicate dirstate entry in read"
500 "duplicate dirstate entry in read"
501 );
501 );
502 node.data = NodeData::Entry(*entry);
502 node.data = NodeData::Entry(*entry);
503 node.copy_source = copy_source.map(Cow::Borrowed);
503 node.copy_source = copy_source.map(Cow::Borrowed);
504 map.nodes_with_entry_count += 1;
504 map.nodes_with_entry_count += 1;
505 if copy_source.is_some() {
505 if copy_source.is_some() {
506 map.nodes_with_copy_source_count += 1
506 map.nodes_with_copy_source_count += 1
507 }
507 }
508 Ok(())
508 Ok(())
509 },
509 },
510 )?;
510 )?;
511 let parents = Some(parents.clone());
511 let parents = Some(parents.clone());
512
512
513 Ok((map, parents))
513 Ok((map, parents))
514 }
514 }
515
515
516 /// Assuming dirstate-v2 format, returns whether the next write should
516 /// Assuming dirstate-v2 format, returns whether the next write should
517 /// append to the existing data file that contains `self.on_disk` (true),
517 /// append to the existing data file that contains `self.on_disk` (true),
518 /// or create a new data file from scratch (false).
518 /// or create a new data file from scratch (false).
519 pub(super) fn write_should_append(&self) -> bool {
519 pub(super) fn write_should_append(&self) -> bool {
520 let ratio = self.unreachable_bytes as f32 / self.on_disk.len() as f32;
520 let ratio = self.unreachable_bytes as f32 / self.on_disk.len() as f32;
521 ratio < ACCEPTABLE_UNREACHABLE_BYTES_RATIO
521 ratio < ACCEPTABLE_UNREACHABLE_BYTES_RATIO
522 }
522 }
523
523
524 fn get_node<'tree>(
524 fn get_node<'tree>(
525 &'tree self,
525 &'tree self,
526 path: &HgPath,
526 path: &HgPath,
527 ) -> Result<Option<NodeRef<'tree, 'on_disk>>, DirstateV2ParseError> {
527 ) -> Result<Option<NodeRef<'tree, 'on_disk>>, DirstateV2ParseError> {
528 let mut children = self.root.as_ref();
528 let mut children = self.root.as_ref();
529 let mut components = path.components();
529 let mut components = path.components();
530 let mut component =
530 let mut component =
531 components.next().expect("expected at least one components");
531 components.next().expect("expected at least one components");
532 loop {
532 loop {
533 if let Some(child) = children.get(component, self.on_disk)? {
533 if let Some(child) = children.get(component, self.on_disk)? {
534 if let Some(next_component) = components.next() {
534 if let Some(next_component) = components.next() {
535 component = next_component;
535 component = next_component;
536 children = child.children(self.on_disk)?;
536 children = child.children(self.on_disk)?;
537 } else {
537 } else {
538 return Ok(Some(child));
538 return Ok(Some(child));
539 }
539 }
540 } else {
540 } else {
541 return Ok(None);
541 return Ok(None);
542 }
542 }
543 }
543 }
544 }
544 }
545
545
546 /// Returns a mutable reference to the node at `path` if it exists
546 /// Returns a mutable reference to the node at `path` if it exists
547 ///
547 ///
548 /// This takes `root` instead of `&mut self` so that callers can mutate
548 /// This takes `root` instead of `&mut self` so that callers can mutate
549 /// other fields while the returned borrow is still valid
549 /// other fields while the returned borrow is still valid.
550 ///
551 /// `each_ancestor` is a callback that is called for each ancestor node
552 /// when descending the tree. It is used to keep the different counters
553 /// of the `DirstateMap` up-to-date.
550 fn get_node_mut<'tree>(
554 fn get_node_mut<'tree>(
551 on_disk: &'on_disk [u8],
555 on_disk: &'on_disk [u8],
552 unreachable_bytes: &mut u32,
556 unreachable_bytes: &mut u32,
553 root: &'tree mut ChildNodes<'on_disk>,
557 root: &'tree mut ChildNodes<'on_disk>,
554 path: &HgPath,
558 path: &HgPath,
559 mut each_ancestor: impl FnMut(&mut Node),
555 ) -> Result<Option<&'tree mut Node<'on_disk>>, DirstateV2ParseError> {
560 ) -> Result<Option<&'tree mut Node<'on_disk>>, DirstateV2ParseError> {
556 let mut children = root;
561 let mut children = root;
557 let mut components = path.components();
562 let mut components = path.components();
558 let mut component =
563 let mut component =
559 components.next().expect("expected at least one components");
564 components.next().expect("expected at least one components");
560 loop {
565 loop {
561 if let Some(child) = children
566 if let Some(child) = children
562 .make_mut(on_disk, unreachable_bytes)?
567 .make_mut(on_disk, unreachable_bytes)?
563 .get_mut(component)
568 .get_mut(component)
564 {
569 {
565 if let Some(next_component) = components.next() {
570 if let Some(next_component) = components.next() {
571 each_ancestor(child);
566 component = next_component;
572 component = next_component;
567 children = &mut child.children;
573 children = &mut child.children;
568 } else {
574 } else {
569 return Ok(Some(child));
575 return Ok(Some(child));
570 }
576 }
571 } else {
577 } else {
572 return Ok(None);
578 return Ok(None);
573 }
579 }
574 }
580 }
575 }
581 }
576
582
577 /// Get a mutable reference to the node at `path`, creating it if it does
583 /// Get a mutable reference to the node at `path`, creating it if it does
578 /// not exist.
584 /// not exist.
579 ///
585 ///
580 /// `each_ancestor` is a callback that is called for each ancestor node
586 /// `each_ancestor` is a callback that is called for each ancestor node
581 /// when descending the tree. It is used to keep the different counters
587 /// when descending the tree. It is used to keep the different counters
582 /// of the `DirstateMap` up-to-date.
588 /// of the `DirstateMap` up-to-date.
583 fn get_or_insert_node<'tree, 'path>(
589 fn get_or_insert_node<'tree, 'path>(
584 &'tree mut self,
590 &'tree mut self,
585 path: &'path HgPath,
591 path: &'path HgPath,
586 each_ancestor: impl FnMut(&mut Node),
592 each_ancestor: impl FnMut(&mut Node),
587 ) -> Result<&'tree mut Node<'on_disk>, DirstateV2ParseError> {
593 ) -> Result<&'tree mut Node<'on_disk>, DirstateV2ParseError> {
588 Self::get_or_insert_node_inner(
594 Self::get_or_insert_node_inner(
589 self.on_disk,
595 self.on_disk,
590 &mut self.unreachable_bytes,
596 &mut self.unreachable_bytes,
591 &mut self.root,
597 &mut self.root,
592 path,
598 path,
593 WithBasename::to_cow_owned,
599 WithBasename::to_cow_owned,
594 each_ancestor,
600 each_ancestor,
595 )
601 )
596 }
602 }
597
603
598 /// Lower-level version of `get_or_insert_node_inner`, which is used when
604 /// Lower-level version of `get_or_insert_node_inner`, which is used when
599 /// parsing disk data to remove allocations for new nodes.
605 /// parsing disk data to remove allocations for new nodes.
600 fn get_or_insert_node_inner<'tree, 'path>(
606 fn get_or_insert_node_inner<'tree, 'path>(
601 on_disk: &'on_disk [u8],
607 on_disk: &'on_disk [u8],
602 unreachable_bytes: &mut u32,
608 unreachable_bytes: &mut u32,
603 root: &'tree mut ChildNodes<'on_disk>,
609 root: &'tree mut ChildNodes<'on_disk>,
604 path: &'path HgPath,
610 path: &'path HgPath,
605 to_cow: impl Fn(
611 to_cow: impl Fn(
606 WithBasename<&'path HgPath>,
612 WithBasename<&'path HgPath>,
607 ) -> WithBasename<Cow<'on_disk, HgPath>>,
613 ) -> WithBasename<Cow<'on_disk, HgPath>>,
608 mut each_ancestor: impl FnMut(&mut Node),
614 mut each_ancestor: impl FnMut(&mut Node),
609 ) -> Result<&'tree mut Node<'on_disk>, DirstateV2ParseError> {
615 ) -> Result<&'tree mut Node<'on_disk>, DirstateV2ParseError> {
610 let mut child_nodes = root;
616 let mut child_nodes = root;
611 let mut inclusive_ancestor_paths =
617 let mut inclusive_ancestor_paths =
612 WithBasename::inclusive_ancestors_of(path);
618 WithBasename::inclusive_ancestors_of(path);
613 let mut ancestor_path = inclusive_ancestor_paths
619 let mut ancestor_path = inclusive_ancestor_paths
614 .next()
620 .next()
615 .expect("expected at least one inclusive ancestor");
621 .expect("expected at least one inclusive ancestor");
616 loop {
622 loop {
617 let (_, child_node) = child_nodes
623 let (_, child_node) = child_nodes
618 .make_mut(on_disk, unreachable_bytes)?
624 .make_mut(on_disk, unreachable_bytes)?
619 .raw_entry_mut()
625 .raw_entry_mut()
620 .from_key(ancestor_path.base_name())
626 .from_key(ancestor_path.base_name())
621 .or_insert_with(|| (to_cow(ancestor_path), Node::default()));
627 .or_insert_with(|| (to_cow(ancestor_path), Node::default()));
622 if let Some(next) = inclusive_ancestor_paths.next() {
628 if let Some(next) = inclusive_ancestor_paths.next() {
623 each_ancestor(child_node);
629 each_ancestor(child_node);
624 ancestor_path = next;
630 ancestor_path = next;
625 child_nodes = &mut child_node.children;
631 child_nodes = &mut child_node.children;
626 } else {
632 } else {
627 return Ok(child_node);
633 return Ok(child_node);
628 }
634 }
629 }
635 }
630 }
636 }
631
637
632 fn reset_state(
638 fn reset_state(
633 &mut self,
639 &mut self,
634 filename: &HgPath,
640 filename: &HgPath,
635 old_entry_opt: Option<DirstateEntry>,
641 old_entry_opt: Option<DirstateEntry>,
636 wc_tracked: bool,
642 wc_tracked: bool,
637 p1_tracked: bool,
643 p1_tracked: bool,
638 p2_info: bool,
644 p2_info: bool,
639 has_meaningful_mtime: bool,
645 has_meaningful_mtime: bool,
640 parent_file_data_opt: Option<ParentFileData>,
646 parent_file_data_opt: Option<ParentFileData>,
641 ) -> Result<(), DirstateError> {
647 ) -> Result<(), DirstateError> {
642 let (had_entry, was_tracked) = match old_entry_opt {
648 let (had_entry, was_tracked) = match old_entry_opt {
643 Some(old_entry) => (true, old_entry.tracked()),
649 Some(old_entry) => (true, old_entry.tracked()),
644 None => (false, false),
650 None => (false, false),
645 };
651 };
646 let node = self.get_or_insert_node(filename, |ancestor| {
652 let node = self.get_or_insert_node(filename, |ancestor| {
647 if !had_entry {
653 if !had_entry {
648 ancestor.descendants_with_entry_count += 1;
654 ancestor.descendants_with_entry_count += 1;
649 }
655 }
650 if was_tracked {
656 if was_tracked {
651 if !wc_tracked {
657 if !wc_tracked {
652 ancestor.tracked_descendants_count = ancestor
658 ancestor.tracked_descendants_count = ancestor
653 .tracked_descendants_count
659 .tracked_descendants_count
654 .checked_sub(1)
660 .checked_sub(1)
655 .expect("tracked count to be >= 0");
661 .expect("tracked count to be >= 0");
656 }
662 }
657 } else {
663 } else {
658 if wc_tracked {
664 if wc_tracked {
659 ancestor.tracked_descendants_count += 1;
665 ancestor.tracked_descendants_count += 1;
660 }
666 }
661 }
667 }
662 })?;
668 })?;
663
669
664 let v2_data = if let Some(parent_file_data) = parent_file_data_opt {
670 let v2_data = if let Some(parent_file_data) = parent_file_data_opt {
665 DirstateV2Data {
671 DirstateV2Data {
666 wc_tracked,
672 wc_tracked,
667 p1_tracked,
673 p1_tracked,
668 p2_info,
674 p2_info,
669 mode_size: parent_file_data.mode_size,
675 mode_size: parent_file_data.mode_size,
670 mtime: if has_meaningful_mtime {
676 mtime: if has_meaningful_mtime {
671 parent_file_data.mtime
677 parent_file_data.mtime
672 } else {
678 } else {
673 None
679 None
674 },
680 },
675 ..Default::default()
681 ..Default::default()
676 }
682 }
677 } else {
683 } else {
678 DirstateV2Data {
684 DirstateV2Data {
679 wc_tracked,
685 wc_tracked,
680 p1_tracked,
686 p1_tracked,
681 p2_info,
687 p2_info,
682 ..Default::default()
688 ..Default::default()
683 }
689 }
684 };
690 };
685 node.data = NodeData::Entry(DirstateEntry::from_v2_data(v2_data));
691 node.data = NodeData::Entry(DirstateEntry::from_v2_data(v2_data));
686 if !had_entry {
692 if !had_entry {
687 self.nodes_with_entry_count += 1;
693 self.nodes_with_entry_count += 1;
688 }
694 }
689 Ok(())
695 Ok(())
690 }
696 }
691
697
692 fn set_tracked(
698 fn set_tracked(
693 &mut self,
699 &mut self,
694 filename: &HgPath,
700 filename: &HgPath,
695 old_entry_opt: Option<DirstateEntry>,
701 old_entry_opt: Option<DirstateEntry>,
696 ) -> Result<bool, DirstateV2ParseError> {
702 ) -> Result<bool, DirstateV2ParseError> {
697 let was_tracked = old_entry_opt.map_or(false, |e| e.tracked());
703 let was_tracked = old_entry_opt.map_or(false, |e| e.tracked());
698 let had_entry = old_entry_opt.is_some();
704 let had_entry = old_entry_opt.is_some();
699 let tracked_count_increment = if was_tracked { 0 } else { 1 };
705 let tracked_count_increment = if was_tracked { 0 } else { 1 };
700 let mut new = false;
706 let mut new = false;
701
707
702 let node = self.get_or_insert_node(filename, |ancestor| {
708 let node = self.get_or_insert_node(filename, |ancestor| {
703 if !had_entry {
709 if !had_entry {
704 ancestor.descendants_with_entry_count += 1;
710 ancestor.descendants_with_entry_count += 1;
705 }
711 }
706
712
707 ancestor.tracked_descendants_count += tracked_count_increment;
713 ancestor.tracked_descendants_count += tracked_count_increment;
708 })?;
714 })?;
709 if let Some(old_entry) = old_entry_opt {
715 if let Some(old_entry) = old_entry_opt {
710 let mut e = old_entry.clone();
716 let mut e = old_entry.clone();
711 if e.tracked() {
717 if e.tracked() {
712 // XXX
718 // XXX
713 // This is probably overkill for more case, but we need this to
719 // This is probably overkill for more case, but we need this to
714 // fully replace the `normallookup` call with `set_tracked`
720 // fully replace the `normallookup` call with `set_tracked`
715 // one. Consider smoothing this in the future.
721 // one. Consider smoothing this in the future.
716 e.set_possibly_dirty();
722 e.set_possibly_dirty();
717 } else {
723 } else {
718 new = true;
724 new = true;
719 e.set_tracked();
725 e.set_tracked();
720 }
726 }
721 node.data = NodeData::Entry(e)
727 node.data = NodeData::Entry(e)
722 } else {
728 } else {
723 node.data = NodeData::Entry(DirstateEntry::new_tracked());
729 node.data = NodeData::Entry(DirstateEntry::new_tracked());
724 self.nodes_with_entry_count += 1;
730 self.nodes_with_entry_count += 1;
725 new = true;
731 new = true;
726 };
732 };
727 Ok(new)
733 Ok(new)
728 }
734 }
729
735
730 /// It is the responsibility of the caller to know that there was an entry
736 /// It is the responsibility of the caller to know that there was an entry
731 /// there before. Does not handle the removal of copy source
737 /// there before. Does not handle the removal of copy source
732 fn set_untracked(
738 fn set_untracked(
733 &mut self,
739 &mut self,
734 filename: &HgPath,
740 filename: &HgPath,
735 old_entry: DirstateEntry,
741 old_entry: DirstateEntry,
736 ) -> Result<(), DirstateV2ParseError> {
742 ) -> Result<(), DirstateV2ParseError> {
737 let node = self.get_or_insert_node(filename, |ancestor| {
743 let node = self.get_or_insert_node(filename, |ancestor| {
738 ancestor.tracked_descendants_count = ancestor
744 ancestor.tracked_descendants_count = ancestor
739 .tracked_descendants_count
745 .tracked_descendants_count
740 .checked_sub(1)
746 .checked_sub(1)
741 .expect("tracked_descendants_count should be >= 0");
747 .expect("tracked_descendants_count should be >= 0");
742 })?;
748 })?;
743 let mut new_entry = old_entry.clone();
749 let mut new_entry = old_entry.clone();
744 new_entry.set_untracked();
750 new_entry.set_untracked();
745 node.data = NodeData::Entry(new_entry);
751 node.data = NodeData::Entry(new_entry);
746 Ok(())
752 Ok(())
747 }
753 }
748
754
749 fn set_clean(
755 fn set_clean(
750 &mut self,
756 &mut self,
751 filename: &HgPath,
757 filename: &HgPath,
752 old_entry: DirstateEntry,
758 old_entry: DirstateEntry,
753 mode: u32,
759 mode: u32,
754 size: u32,
760 size: u32,
755 mtime: TruncatedTimestamp,
761 mtime: TruncatedTimestamp,
756 ) -> Result<(), DirstateError> {
762 ) -> Result<(), DirstateError> {
757 let node = self.get_or_insert_node(filename, |ancestor| {
763 let node = self.get_or_insert_node(filename, |ancestor| {
758 if !old_entry.tracked() {
764 if !old_entry.tracked() {
759 ancestor.tracked_descendants_count += 1;
765 ancestor.tracked_descendants_count += 1;
760 }
766 }
761 })?;
767 })?;
762 let mut new_entry = old_entry.clone();
768 let mut new_entry = old_entry.clone();
763 new_entry.set_clean(mode, size, mtime);
769 new_entry.set_clean(mode, size, mtime);
764 node.data = NodeData::Entry(new_entry);
770 node.data = NodeData::Entry(new_entry);
765 Ok(())
771 Ok(())
766 }
772 }
767
773
768 fn set_possibly_dirty(
774 fn set_possibly_dirty(
769 &mut self,
775 &mut self,
770 filename: &HgPath,
776 filename: &HgPath,
771 ) -> Result<(), DirstateError> {
777 ) -> Result<(), DirstateError> {
772 let node = self.get_or_insert_node(filename, |_ancestor| {})?;
778 let node = self.get_or_insert_node(filename, |_ancestor| {})?;
773 let entry = node.data.as_entry_mut().expect("entry should exist");
779 let entry = node.data.as_entry_mut().expect("entry should exist");
774 entry.set_possibly_dirty();
780 entry.set_possibly_dirty();
775 node.data = NodeData::Entry(*entry);
781 node.data = NodeData::Entry(*entry);
776 Ok(())
782 Ok(())
777 }
783 }
778
784
779 /// Clears the cached mtime for the (potential) folder at `path`.
785 /// Clears the cached mtime for the (potential) folder at `path`.
780 pub(super) fn clear_cached_mtime(
786 pub(super) fn clear_cached_mtime(
781 &mut self,
787 &mut self,
782 path: &HgPath,
788 path: &HgPath,
783 ) -> Result<(), DirstateV2ParseError> {
789 ) -> Result<(), DirstateV2ParseError> {
784 let node = match DirstateMap::get_node_mut(
790 let node = match DirstateMap::get_node_mut(
785 self.on_disk,
791 self.on_disk,
786 &mut self.unreachable_bytes,
792 &mut self.unreachable_bytes,
787 &mut self.root,
793 &mut self.root,
788 path,
794 path,
795 |_ancestor| {},
789 )? {
796 )? {
790 Some(node) => node,
797 Some(node) => node,
791 None => return Ok(()),
798 None => return Ok(()),
792 };
799 };
793 if let NodeData::CachedDirectory { .. } = &node.data {
800 if let NodeData::CachedDirectory { .. } = &node.data {
794 node.data = NodeData::None
801 node.data = NodeData::None
795 }
802 }
796 Ok(())
803 Ok(())
797 }
804 }
798
805
799 /// Sets the cached mtime for the (potential) folder at `path`.
806 /// Sets the cached mtime for the (potential) folder at `path`.
800 pub(super) fn set_cached_mtime(
807 pub(super) fn set_cached_mtime(
801 &mut self,
808 &mut self,
802 path: &HgPath,
809 path: &HgPath,
803 mtime: TruncatedTimestamp,
810 mtime: TruncatedTimestamp,
804 ) -> Result<(), DirstateV2ParseError> {
811 ) -> Result<(), DirstateV2ParseError> {
805 let node = match DirstateMap::get_node_mut(
812 let node = match DirstateMap::get_node_mut(
806 self.on_disk,
813 self.on_disk,
807 &mut self.unreachable_bytes,
814 &mut self.unreachable_bytes,
808 &mut self.root,
815 &mut self.root,
809 path,
816 path,
817 |_ancestor| {},
810 )? {
818 )? {
811 Some(node) => node,
819 Some(node) => node,
812 None => return Ok(()),
820 None => return Ok(()),
813 };
821 };
814 match &node.data {
822 match &node.data {
815 NodeData::Entry(_) => {} // Don’t overwrite an entry
823 NodeData::Entry(_) => {} // Don’t overwrite an entry
816 NodeData::CachedDirectory { .. } | NodeData::None => {
824 NodeData::CachedDirectory { .. } | NodeData::None => {
817 node.data = NodeData::CachedDirectory { mtime }
825 node.data = NodeData::CachedDirectory { mtime }
818 }
826 }
819 }
827 }
820 Ok(())
828 Ok(())
821 }
829 }
822
830
823 fn iter_nodes<'tree>(
831 fn iter_nodes<'tree>(
824 &'tree self,
832 &'tree self,
825 ) -> impl Iterator<
833 ) -> impl Iterator<
826 Item = Result<NodeRef<'tree, 'on_disk>, DirstateV2ParseError>,
834 Item = Result<NodeRef<'tree, 'on_disk>, DirstateV2ParseError>,
827 > + 'tree {
835 > + 'tree {
828 // Depth first tree traversal.
836 // Depth first tree traversal.
829 //
837 //
830 // If we could afford internal iteration and recursion,
838 // If we could afford internal iteration and recursion,
831 // this would look like:
839 // this would look like:
832 //
840 //
833 // ```
841 // ```
834 // fn traverse_children(
842 // fn traverse_children(
835 // children: &ChildNodes,
843 // children: &ChildNodes,
836 // each: &mut impl FnMut(&Node),
844 // each: &mut impl FnMut(&Node),
837 // ) {
845 // ) {
838 // for child in children.values() {
846 // for child in children.values() {
839 // traverse_children(&child.children, each);
847 // traverse_children(&child.children, each);
840 // each(child);
848 // each(child);
841 // }
849 // }
842 // }
850 // }
843 // ```
851 // ```
844 //
852 //
845 // However we want an external iterator and therefore can’t use the
853 // However we want an external iterator and therefore can’t use the
846 // call stack. Use an explicit stack instead:
854 // call stack. Use an explicit stack instead:
847 let mut stack = Vec::new();
855 let mut stack = Vec::new();
848 let mut iter = self.root.as_ref().iter();
856 let mut iter = self.root.as_ref().iter();
849 std::iter::from_fn(move || {
857 std::iter::from_fn(move || {
850 while let Some(child_node) = iter.next() {
858 while let Some(child_node) = iter.next() {
851 let children = match child_node.children(self.on_disk) {
859 let children = match child_node.children(self.on_disk) {
852 Ok(children) => children,
860 Ok(children) => children,
853 Err(error) => return Some(Err(error)),
861 Err(error) => return Some(Err(error)),
854 };
862 };
855 // Pseudo-recursion
863 // Pseudo-recursion
856 let new_iter = children.iter();
864 let new_iter = children.iter();
857 let old_iter = std::mem::replace(&mut iter, new_iter);
865 let old_iter = std::mem::replace(&mut iter, new_iter);
858 stack.push((child_node, old_iter));
866 stack.push((child_node, old_iter));
859 }
867 }
860 // Found the end of a `children.iter()` iterator.
868 // Found the end of a `children.iter()` iterator.
861 if let Some((child_node, next_iter)) = stack.pop() {
869 if let Some((child_node, next_iter)) = stack.pop() {
862 // "Return" from pseudo-recursion by restoring state from the
870 // "Return" from pseudo-recursion by restoring state from the
863 // explicit stack
871 // explicit stack
864 iter = next_iter;
872 iter = next_iter;
865
873
866 Some(Ok(child_node))
874 Some(Ok(child_node))
867 } else {
875 } else {
868 // Reached the bottom of the stack, we’re done
876 // Reached the bottom of the stack, we’re done
869 None
877 None
870 }
878 }
871 })
879 })
872 }
880 }
873
881
874 fn count_dropped_path(unreachable_bytes: &mut u32, path: &Cow<HgPath>) {
882 fn count_dropped_path(unreachable_bytes: &mut u32, path: &Cow<HgPath>) {
875 if let Cow::Borrowed(path) = path {
883 if let Cow::Borrowed(path) = path {
876 *unreachable_bytes += path.len() as u32
884 *unreachable_bytes += path.len() as u32
877 }
885 }
878 }
886 }
879 }
887 }
880
888
881 /// Like `Iterator::filter_map`, but over a fallible iterator of `Result`s.
889 /// Like `Iterator::filter_map`, but over a fallible iterator of `Result`s.
882 ///
890 ///
883 /// The callback is only called for incoming `Ok` values. Errors are passed
891 /// The callback is only called for incoming `Ok` values. Errors are passed
884 /// through as-is. In order to let it use the `?` operator the callback is
892 /// through as-is. In order to let it use the `?` operator the callback is
885 /// expected to return a `Result` of `Option`, instead of an `Option` of
893 /// expected to return a `Result` of `Option`, instead of an `Option` of
886 /// `Result`.
894 /// `Result`.
887 fn filter_map_results<'a, I, F, A, B, E>(
895 fn filter_map_results<'a, I, F, A, B, E>(
888 iter: I,
896 iter: I,
889 f: F,
897 f: F,
890 ) -> impl Iterator<Item = Result<B, E>> + 'a
898 ) -> impl Iterator<Item = Result<B, E>> + 'a
891 where
899 where
892 I: Iterator<Item = Result<A, E>> + 'a,
900 I: Iterator<Item = Result<A, E>> + 'a,
893 F: Fn(A) -> Result<Option<B>, E> + 'a,
901 F: Fn(A) -> Result<Option<B>, E> + 'a,
894 {
902 {
895 iter.filter_map(move |result| match result {
903 iter.filter_map(move |result| match result {
896 Ok(node) => f(node).transpose(),
904 Ok(node) => f(node).transpose(),
897 Err(e) => Some(Err(e)),
905 Err(e) => Some(Err(e)),
898 })
906 })
899 }
907 }
900
908
901 impl OwningDirstateMap {
909 impl OwningDirstateMap {
902 pub fn clear(&mut self) {
910 pub fn clear(&mut self) {
903 self.with_dmap_mut(|map| {
911 self.with_dmap_mut(|map| {
904 map.root = Default::default();
912 map.root = Default::default();
905 map.nodes_with_entry_count = 0;
913 map.nodes_with_entry_count = 0;
906 map.nodes_with_copy_source_count = 0;
914 map.nodes_with_copy_source_count = 0;
907 });
915 });
908 }
916 }
909
917
910 pub fn set_tracked(
918 pub fn set_tracked(
911 &mut self,
919 &mut self,
912 filename: &HgPath,
920 filename: &HgPath,
913 ) -> Result<bool, DirstateV2ParseError> {
921 ) -> Result<bool, DirstateV2ParseError> {
914 let old_entry_opt = self.get(filename)?;
922 let old_entry_opt = self.get(filename)?;
915 self.with_dmap_mut(|map| map.set_tracked(filename, old_entry_opt))
923 self.with_dmap_mut(|map| map.set_tracked(filename, old_entry_opt))
916 }
924 }
917
925
918 pub fn set_untracked(
926 pub fn set_untracked(
919 &mut self,
927 &mut self,
920 filename: &HgPath,
928 filename: &HgPath,
921 ) -> Result<bool, DirstateError> {
929 ) -> Result<bool, DirstateError> {
922 let old_entry_opt = self.get(filename)?;
930 let old_entry_opt = self.get(filename)?;
923 match old_entry_opt {
931 match old_entry_opt {
924 None => Ok(false),
932 None => Ok(false),
925 Some(old_entry) => {
933 Some(old_entry) => {
926 if !old_entry.tracked() {
934 if !old_entry.tracked() {
927 // `DirstateMap::set_untracked` is not a noop if
935 // `DirstateMap::set_untracked` is not a noop if
928 // already not tracked as it will decrement the
936 // already not tracked as it will decrement the
929 // tracked counters while going down.
937 // tracked counters while going down.
930 return Ok(true);
938 return Ok(true);
931 }
939 }
932 if old_entry.added() {
940 if old_entry.added() {
933 // Untracking an "added" entry will just result in a
941 // Untracking an "added" entry will just result in a
934 // worthless entry (and other parts of the code will
942 // worthless entry (and other parts of the code will
935 // complain about it), just drop it entirely.
943 // complain about it), just drop it entirely.
936 self.drop_entry_and_copy_source(filename)?;
944 self.drop_entry_and_copy_source(filename)?;
937 return Ok(true);
945 return Ok(true);
938 }
946 }
939 if !old_entry.p2_info() {
947 if !old_entry.p2_info() {
940 self.copy_map_remove(filename)?;
948 self.copy_map_remove(filename)?;
941 }
949 }
942
950
943 self.with_dmap_mut(|map| {
951 self.with_dmap_mut(|map| {
944 map.set_untracked(filename, old_entry)?;
952 map.set_untracked(filename, old_entry)?;
945 Ok(true)
953 Ok(true)
946 })
954 })
947 }
955 }
948 }
956 }
949 }
957 }
950
958
951 pub fn set_clean(
959 pub fn set_clean(
952 &mut self,
960 &mut self,
953 filename: &HgPath,
961 filename: &HgPath,
954 mode: u32,
962 mode: u32,
955 size: u32,
963 size: u32,
956 mtime: TruncatedTimestamp,
964 mtime: TruncatedTimestamp,
957 ) -> Result<(), DirstateError> {
965 ) -> Result<(), DirstateError> {
958 let old_entry = match self.get(filename)? {
966 let old_entry = match self.get(filename)? {
959 None => {
967 None => {
960 return Err(
968 return Err(
961 DirstateMapError::PathNotFound(filename.into()).into()
969 DirstateMapError::PathNotFound(filename.into()).into()
962 )
970 )
963 }
971 }
964 Some(e) => e,
972 Some(e) => e,
965 };
973 };
966 self.copy_map_remove(filename)?;
974 self.copy_map_remove(filename)?;
967 self.with_dmap_mut(|map| {
975 self.with_dmap_mut(|map| {
968 map.set_clean(filename, old_entry, mode, size, mtime)
976 map.set_clean(filename, old_entry, mode, size, mtime)
969 })
977 })
970 }
978 }
971
979
972 pub fn set_possibly_dirty(
980 pub fn set_possibly_dirty(
973 &mut self,
981 &mut self,
974 filename: &HgPath,
982 filename: &HgPath,
975 ) -> Result<(), DirstateError> {
983 ) -> Result<(), DirstateError> {
976 if self.get(filename)?.is_none() {
984 if self.get(filename)?.is_none() {
977 return Err(DirstateMapError::PathNotFound(filename.into()).into());
985 return Err(DirstateMapError::PathNotFound(filename.into()).into());
978 }
986 }
979 self.with_dmap_mut(|map| map.set_possibly_dirty(filename))
987 self.with_dmap_mut(|map| map.set_possibly_dirty(filename))
980 }
988 }
981
989
982 pub fn reset_state(
990 pub fn reset_state(
983 &mut self,
991 &mut self,
984 filename: &HgPath,
992 filename: &HgPath,
985 wc_tracked: bool,
993 wc_tracked: bool,
986 p1_tracked: bool,
994 p1_tracked: bool,
987 p2_info: bool,
995 p2_info: bool,
988 has_meaningful_mtime: bool,
996 has_meaningful_mtime: bool,
989 parent_file_data_opt: Option<ParentFileData>,
997 parent_file_data_opt: Option<ParentFileData>,
990 ) -> Result<(), DirstateError> {
998 ) -> Result<(), DirstateError> {
991 if !(p1_tracked || p2_info || wc_tracked) {
999 if !(p1_tracked || p2_info || wc_tracked) {
992 self.drop_entry_and_copy_source(filename)?;
1000 self.drop_entry_and_copy_source(filename)?;
993 return Ok(());
1001 return Ok(());
994 }
1002 }
995 self.copy_map_remove(filename)?;
1003 self.copy_map_remove(filename)?;
996 let old_entry_opt = self.get(filename)?;
1004 let old_entry_opt = self.get(filename)?;
997 self.with_dmap_mut(|map| {
1005 self.with_dmap_mut(|map| {
998 map.reset_state(
1006 map.reset_state(
999 filename,
1007 filename,
1000 old_entry_opt,
1008 old_entry_opt,
1001 wc_tracked,
1009 wc_tracked,
1002 p1_tracked,
1010 p1_tracked,
1003 p2_info,
1011 p2_info,
1004 has_meaningful_mtime,
1012 has_meaningful_mtime,
1005 parent_file_data_opt,
1013 parent_file_data_opt,
1006 )
1014 )
1007 })
1015 })
1008 }
1016 }
1009
1017
1010 pub fn drop_entry_and_copy_source(
1018 pub fn drop_entry_and_copy_source(
1011 &mut self,
1019 &mut self,
1012 filename: &HgPath,
1020 filename: &HgPath,
1013 ) -> Result<(), DirstateError> {
1021 ) -> Result<(), DirstateError> {
1014 let was_tracked = self.get(filename)?.map_or(false, |e| e.tracked());
1022 let was_tracked = self.get(filename)?.map_or(false, |e| e.tracked());
1015 struct Dropped {
1023 struct Dropped {
1016 was_tracked: bool,
1024 was_tracked: bool,
1017 had_entry: bool,
1025 had_entry: bool,
1018 had_copy_source: bool,
1026 had_copy_source: bool,
1019 }
1027 }
1020
1028
1021 /// If this returns `Ok(Some((dropped, removed)))`, then
1029 /// If this returns `Ok(Some((dropped, removed)))`, then
1022 ///
1030 ///
1023 /// * `dropped` is about the leaf node that was at `filename`
1031 /// * `dropped` is about the leaf node that was at `filename`
1024 /// * `removed` is whether this particular level of recursion just
1032 /// * `removed` is whether this particular level of recursion just
1025 /// removed a node in `nodes`.
1033 /// removed a node in `nodes`.
1026 fn recur<'on_disk>(
1034 fn recur<'on_disk>(
1027 on_disk: &'on_disk [u8],
1035 on_disk: &'on_disk [u8],
1028 unreachable_bytes: &mut u32,
1036 unreachable_bytes: &mut u32,
1029 nodes: &mut ChildNodes<'on_disk>,
1037 nodes: &mut ChildNodes<'on_disk>,
1030 path: &HgPath,
1038 path: &HgPath,
1031 ) -> Result<Option<(Dropped, bool)>, DirstateV2ParseError> {
1039 ) -> Result<Option<(Dropped, bool)>, DirstateV2ParseError> {
1032 let (first_path_component, rest_of_path) =
1040 let (first_path_component, rest_of_path) =
1033 path.split_first_component();
1041 path.split_first_component();
1034 let nodes = nodes.make_mut(on_disk, unreachable_bytes)?;
1042 let nodes = nodes.make_mut(on_disk, unreachable_bytes)?;
1035 let node = if let Some(node) = nodes.get_mut(first_path_component)
1043 let node = if let Some(node) = nodes.get_mut(first_path_component)
1036 {
1044 {
1037 node
1045 node
1038 } else {
1046 } else {
1039 return Ok(None);
1047 return Ok(None);
1040 };
1048 };
1041 let dropped;
1049 let dropped;
1042 if let Some(rest) = rest_of_path {
1050 if let Some(rest) = rest_of_path {
1043 if let Some((d, removed)) = recur(
1051 if let Some((d, removed)) = recur(
1044 on_disk,
1052 on_disk,
1045 unreachable_bytes,
1053 unreachable_bytes,
1046 &mut node.children,
1054 &mut node.children,
1047 rest,
1055 rest,
1048 )? {
1056 )? {
1049 dropped = d;
1057 dropped = d;
1050 if dropped.had_entry {
1058 if dropped.had_entry {
1051 node.descendants_with_entry_count = node
1059 node.descendants_with_entry_count = node
1052 .descendants_with_entry_count
1060 .descendants_with_entry_count
1053 .checked_sub(1)
1061 .checked_sub(1)
1054 .expect(
1062 .expect(
1055 "descendants_with_entry_count should be >= 0",
1063 "descendants_with_entry_count should be >= 0",
1056 );
1064 );
1057 }
1065 }
1058 if dropped.was_tracked {
1066 if dropped.was_tracked {
1059 node.tracked_descendants_count = node
1067 node.tracked_descendants_count = node
1060 .tracked_descendants_count
1068 .tracked_descendants_count
1061 .checked_sub(1)
1069 .checked_sub(1)
1062 .expect(
1070 .expect(
1063 "tracked_descendants_count should be >= 0",
1071 "tracked_descendants_count should be >= 0",
1064 );
1072 );
1065 }
1073 }
1066
1074
1067 // Directory caches must be invalidated when removing a
1075 // Directory caches must be invalidated when removing a
1068 // child node
1076 // child node
1069 if removed {
1077 if removed {
1070 if let NodeData::CachedDirectory { .. } = &node.data {
1078 if let NodeData::CachedDirectory { .. } = &node.data {
1071 node.data = NodeData::None
1079 node.data = NodeData::None
1072 }
1080 }
1073 }
1081 }
1074 } else {
1082 } else {
1075 return Ok(None);
1083 return Ok(None);
1076 }
1084 }
1077 } else {
1085 } else {
1078 let entry = node.data.as_entry();
1086 let entry = node.data.as_entry();
1079 let was_tracked = entry.map_or(false, |entry| entry.tracked());
1087 let was_tracked = entry.map_or(false, |entry| entry.tracked());
1080 let had_entry = entry.is_some();
1088 let had_entry = entry.is_some();
1081 if had_entry {
1089 if had_entry {
1082 node.data = NodeData::None
1090 node.data = NodeData::None
1083 }
1091 }
1084 let mut had_copy_source = false;
1092 let mut had_copy_source = false;
1085 if let Some(source) = &node.copy_source {
1093 if let Some(source) = &node.copy_source {
1086 DirstateMap::count_dropped_path(unreachable_bytes, source);
1094 DirstateMap::count_dropped_path(unreachable_bytes, source);
1087 had_copy_source = true;
1095 had_copy_source = true;
1088 node.copy_source = None
1096 node.copy_source = None
1089 }
1097 }
1090 dropped = Dropped {
1098 dropped = Dropped {
1091 was_tracked,
1099 was_tracked,
1092 had_entry,
1100 had_entry,
1093 had_copy_source,
1101 had_copy_source,
1094 };
1102 };
1095 }
1103 }
1096 // After recursion, for both leaf (rest_of_path is None) nodes and
1104 // After recursion, for both leaf (rest_of_path is None) nodes and
1097 // parent nodes, remove a node if it just became empty.
1105 // parent nodes, remove a node if it just became empty.
1098 let remove = !node.data.has_entry()
1106 let remove = !node.data.has_entry()
1099 && node.copy_source.is_none()
1107 && node.copy_source.is_none()
1100 && node.children.is_empty();
1108 && node.children.is_empty();
1101 if remove {
1109 if remove {
1102 let (key, _) =
1110 let (key, _) =
1103 nodes.remove_entry(first_path_component).unwrap();
1111 nodes.remove_entry(first_path_component).unwrap();
1104 DirstateMap::count_dropped_path(
1112 DirstateMap::count_dropped_path(
1105 unreachable_bytes,
1113 unreachable_bytes,
1106 key.full_path(),
1114 key.full_path(),
1107 )
1115 )
1108 }
1116 }
1109 Ok(Some((dropped, remove)))
1117 Ok(Some((dropped, remove)))
1110 }
1118 }
1111
1119
1112 self.with_dmap_mut(|map| {
1120 self.with_dmap_mut(|map| {
1113 if let Some((dropped, _removed)) = recur(
1121 if let Some((dropped, _removed)) = recur(
1114 map.on_disk,
1122 map.on_disk,
1115 &mut map.unreachable_bytes,
1123 &mut map.unreachable_bytes,
1116 &mut map.root,
1124 &mut map.root,
1117 filename,
1125 filename,
1118 )? {
1126 )? {
1119 if dropped.had_entry {
1127 if dropped.had_entry {
1120 map.nodes_with_entry_count = map
1128 map.nodes_with_entry_count = map
1121 .nodes_with_entry_count
1129 .nodes_with_entry_count
1122 .checked_sub(1)
1130 .checked_sub(1)
1123 .expect("nodes_with_entry_count should be >= 0");
1131 .expect("nodes_with_entry_count should be >= 0");
1124 }
1132 }
1125 if dropped.had_copy_source {
1133 if dropped.had_copy_source {
1126 map.nodes_with_copy_source_count = map
1134 map.nodes_with_copy_source_count = map
1127 .nodes_with_copy_source_count
1135 .nodes_with_copy_source_count
1128 .checked_sub(1)
1136 .checked_sub(1)
1129 .expect("nodes_with_copy_source_count should be >= 0");
1137 .expect("nodes_with_copy_source_count should be >= 0");
1130 }
1138 }
1131 } else {
1139 } else {
1132 debug_assert!(!was_tracked);
1140 debug_assert!(!was_tracked);
1133 }
1141 }
1134 Ok(())
1142 Ok(())
1135 })
1143 })
1136 }
1144 }
1137
1145
1138 pub fn has_tracked_dir(
1146 pub fn has_tracked_dir(
1139 &mut self,
1147 &mut self,
1140 directory: &HgPath,
1148 directory: &HgPath,
1141 ) -> Result<bool, DirstateError> {
1149 ) -> Result<bool, DirstateError> {
1142 self.with_dmap_mut(|map| {
1150 self.with_dmap_mut(|map| {
1143 if let Some(node) = map.get_node(directory)? {
1151 if let Some(node) = map.get_node(directory)? {
1144 // A node without a `DirstateEntry` was created to hold child
1152 // A node without a `DirstateEntry` was created to hold child
1145 // nodes, and is therefore a directory.
1153 // nodes, and is therefore a directory.
1146 let state = node.state()?;
1154 let state = node.state()?;
1147 Ok(state.is_none() && node.tracked_descendants_count() > 0)
1155 Ok(state.is_none() && node.tracked_descendants_count() > 0)
1148 } else {
1156 } else {
1149 Ok(false)
1157 Ok(false)
1150 }
1158 }
1151 })
1159 })
1152 }
1160 }
1153
1161
1154 pub fn has_dir(
1162 pub fn has_dir(
1155 &mut self,
1163 &mut self,
1156 directory: &HgPath,
1164 directory: &HgPath,
1157 ) -> Result<bool, DirstateError> {
1165 ) -> Result<bool, DirstateError> {
1158 self.with_dmap_mut(|map| {
1166 self.with_dmap_mut(|map| {
1159 if let Some(node) = map.get_node(directory)? {
1167 if let Some(node) = map.get_node(directory)? {
1160 // A node without a `DirstateEntry` was created to hold child
1168 // A node without a `DirstateEntry` was created to hold child
1161 // nodes, and is therefore a directory.
1169 // nodes, and is therefore a directory.
1162 let state = node.state()?;
1170 let state = node.state()?;
1163 Ok(state.is_none() && node.descendants_with_entry_count() > 0)
1171 Ok(state.is_none() && node.descendants_with_entry_count() > 0)
1164 } else {
1172 } else {
1165 Ok(false)
1173 Ok(false)
1166 }
1174 }
1167 })
1175 })
1168 }
1176 }
1169
1177
1170 #[timed]
1178 #[timed]
1171 pub fn pack_v1(
1179 pub fn pack_v1(
1172 &self,
1180 &self,
1173 parents: DirstateParents,
1181 parents: DirstateParents,
1174 ) -> Result<Vec<u8>, DirstateError> {
1182 ) -> Result<Vec<u8>, DirstateError> {
1175 let map = self.get_map();
1183 let map = self.get_map();
1176 // Optizimation (to be measured?): pre-compute size to avoid `Vec`
1184 // Optizimation (to be measured?): pre-compute size to avoid `Vec`
1177 // reallocations
1185 // reallocations
1178 let mut size = parents.as_bytes().len();
1186 let mut size = parents.as_bytes().len();
1179 for node in map.iter_nodes() {
1187 for node in map.iter_nodes() {
1180 let node = node?;
1188 let node = node?;
1181 if node.entry()?.is_some() {
1189 if node.entry()?.is_some() {
1182 size += packed_entry_size(
1190 size += packed_entry_size(
1183 node.full_path(map.on_disk)?,
1191 node.full_path(map.on_disk)?,
1184 node.copy_source(map.on_disk)?,
1192 node.copy_source(map.on_disk)?,
1185 );
1193 );
1186 }
1194 }
1187 }
1195 }
1188
1196
1189 let mut packed = Vec::with_capacity(size);
1197 let mut packed = Vec::with_capacity(size);
1190 packed.extend(parents.as_bytes());
1198 packed.extend(parents.as_bytes());
1191
1199
1192 for node in map.iter_nodes() {
1200 for node in map.iter_nodes() {
1193 let node = node?;
1201 let node = node?;
1194 if let Some(entry) = node.entry()? {
1202 if let Some(entry) = node.entry()? {
1195 pack_entry(
1203 pack_entry(
1196 node.full_path(map.on_disk)?,
1204 node.full_path(map.on_disk)?,
1197 &entry,
1205 &entry,
1198 node.copy_source(map.on_disk)?,
1206 node.copy_source(map.on_disk)?,
1199 &mut packed,
1207 &mut packed,
1200 );
1208 );
1201 }
1209 }
1202 }
1210 }
1203 Ok(packed)
1211 Ok(packed)
1204 }
1212 }
1205
1213
1206 /// Returns new data and metadata together with whether that data should be
1214 /// Returns new data and metadata together with whether that data should be
1207 /// appended to the existing data file whose content is at
1215 /// appended to the existing data file whose content is at
1208 /// `map.on_disk` (true), instead of written to a new data file
1216 /// `map.on_disk` (true), instead of written to a new data file
1209 /// (false).
1217 /// (false).
1210 #[timed]
1218 #[timed]
1211 pub fn pack_v2(
1219 pub fn pack_v2(
1212 &self,
1220 &self,
1213 can_append: bool,
1221 can_append: bool,
1214 ) -> Result<(Vec<u8>, on_disk::TreeMetadata, bool), DirstateError> {
1222 ) -> Result<(Vec<u8>, on_disk::TreeMetadata, bool), DirstateError> {
1215 let map = self.get_map();
1223 let map = self.get_map();
1216 on_disk::write(map, can_append)
1224 on_disk::write(map, can_append)
1217 }
1225 }
1218
1226
1219 /// `callback` allows the caller to process and do something with the
1227 /// `callback` allows the caller to process and do something with the
1220 /// results of the status. This is needed to do so efficiently (i.e.
1228 /// results of the status. This is needed to do so efficiently (i.e.
1221 /// without cloning the `DirstateStatus` object with its paths) because
1229 /// without cloning the `DirstateStatus` object with its paths) because
1222 /// we need to borrow from `Self`.
1230 /// we need to borrow from `Self`.
1223 pub fn with_status<R>(
1231 pub fn with_status<R>(
1224 &mut self,
1232 &mut self,
1225 matcher: &(dyn Matcher + Sync),
1233 matcher: &(dyn Matcher + Sync),
1226 root_dir: PathBuf,
1234 root_dir: PathBuf,
1227 ignore_files: Vec<PathBuf>,
1235 ignore_files: Vec<PathBuf>,
1228 options: StatusOptions,
1236 options: StatusOptions,
1229 callback: impl for<'r> FnOnce(
1237 callback: impl for<'r> FnOnce(
1230 Result<(DirstateStatus<'r>, Vec<PatternFileWarning>), StatusError>,
1238 Result<(DirstateStatus<'r>, Vec<PatternFileWarning>), StatusError>,
1231 ) -> R,
1239 ) -> R,
1232 ) -> R {
1240 ) -> R {
1233 self.with_dmap_mut(|map| {
1241 self.with_dmap_mut(|map| {
1234 callback(super::status::status(
1242 callback(super::status::status(
1235 map,
1243 map,
1236 matcher,
1244 matcher,
1237 root_dir,
1245 root_dir,
1238 ignore_files,
1246 ignore_files,
1239 options,
1247 options,
1240 ))
1248 ))
1241 })
1249 })
1242 }
1250 }
1243
1251
1244 pub fn copy_map_len(&self) -> usize {
1252 pub fn copy_map_len(&self) -> usize {
1245 let map = self.get_map();
1253 let map = self.get_map();
1246 map.nodes_with_copy_source_count as usize
1254 map.nodes_with_copy_source_count as usize
1247 }
1255 }
1248
1256
1249 pub fn copy_map_iter(&self) -> CopyMapIter<'_> {
1257 pub fn copy_map_iter(&self) -> CopyMapIter<'_> {
1250 let map = self.get_map();
1258 let map = self.get_map();
1251 Box::new(filter_map_results(map.iter_nodes(), move |node| {
1259 Box::new(filter_map_results(map.iter_nodes(), move |node| {
1252 Ok(if let Some(source) = node.copy_source(map.on_disk)? {
1260 Ok(if let Some(source) = node.copy_source(map.on_disk)? {
1253 Some((node.full_path(map.on_disk)?, source))
1261 Some((node.full_path(map.on_disk)?, source))
1254 } else {
1262 } else {
1255 None
1263 None
1256 })
1264 })
1257 }))
1265 }))
1258 }
1266 }
1259
1267
1260 pub fn copy_map_contains_key(
1268 pub fn copy_map_contains_key(
1261 &self,
1269 &self,
1262 key: &HgPath,
1270 key: &HgPath,
1263 ) -> Result<bool, DirstateV2ParseError> {
1271 ) -> Result<bool, DirstateV2ParseError> {
1264 let map = self.get_map();
1272 let map = self.get_map();
1265 Ok(if let Some(node) = map.get_node(key)? {
1273 Ok(if let Some(node) = map.get_node(key)? {
1266 node.has_copy_source()
1274 node.has_copy_source()
1267 } else {
1275 } else {
1268 false
1276 false
1269 })
1277 })
1270 }
1278 }
1271
1279
1272 pub fn copy_map_get(
1280 pub fn copy_map_get(
1273 &self,
1281 &self,
1274 key: &HgPath,
1282 key: &HgPath,
1275 ) -> Result<Option<&HgPath>, DirstateV2ParseError> {
1283 ) -> Result<Option<&HgPath>, DirstateV2ParseError> {
1276 let map = self.get_map();
1284 let map = self.get_map();
1277 if let Some(node) = map.get_node(key)? {
1285 if let Some(node) = map.get_node(key)? {
1278 if let Some(source) = node.copy_source(map.on_disk)? {
1286 if let Some(source) = node.copy_source(map.on_disk)? {
1279 return Ok(Some(source));
1287 return Ok(Some(source));
1280 }
1288 }
1281 }
1289 }
1282 Ok(None)
1290 Ok(None)
1283 }
1291 }
1284
1292
1285 pub fn copy_map_remove(
1293 pub fn copy_map_remove(
1286 &mut self,
1294 &mut self,
1287 key: &HgPath,
1295 key: &HgPath,
1288 ) -> Result<Option<HgPathBuf>, DirstateV2ParseError> {
1296 ) -> Result<Option<HgPathBuf>, DirstateV2ParseError> {
1289 self.with_dmap_mut(|map| {
1297 self.with_dmap_mut(|map| {
1290 let count = &mut map.nodes_with_copy_source_count;
1298 let count = &mut map.nodes_with_copy_source_count;
1291 let unreachable_bytes = &mut map.unreachable_bytes;
1299 let unreachable_bytes = &mut map.unreachable_bytes;
1292 Ok(DirstateMap::get_node_mut(
1300 Ok(DirstateMap::get_node_mut(
1293 map.on_disk,
1301 map.on_disk,
1294 unreachable_bytes,
1302 unreachable_bytes,
1295 &mut map.root,
1303 &mut map.root,
1296 key,
1304 key,
1305 |_ancestor| {},
1297 )?
1306 )?
1298 .and_then(|node| {
1307 .and_then(|node| {
1299 if let Some(source) = &node.copy_source {
1308 if let Some(source) = &node.copy_source {
1300 *count -= 1;
1309 *count -= 1;
1301 DirstateMap::count_dropped_path(unreachable_bytes, source);
1310 DirstateMap::count_dropped_path(unreachable_bytes, source);
1302 }
1311 }
1303 node.copy_source.take().map(Cow::into_owned)
1312 node.copy_source.take().map(Cow::into_owned)
1304 }))
1313 }))
1305 })
1314 })
1306 }
1315 }
1307
1316
1308 pub fn copy_map_insert(
1317 pub fn copy_map_insert(
1309 &mut self,
1318 &mut self,
1310 key: &HgPath,
1319 key: &HgPath,
1311 value: &HgPath,
1320 value: &HgPath,
1312 ) -> Result<Option<HgPathBuf>, DirstateV2ParseError> {
1321 ) -> Result<Option<HgPathBuf>, DirstateV2ParseError> {
1313 self.with_dmap_mut(|map| {
1322 self.with_dmap_mut(|map| {
1314 let node = map.get_or_insert_node(&key, |_ancestor| {})?;
1323 let node = map.get_or_insert_node(&key, |_ancestor| {})?;
1315 let had_copy_source = node.copy_source.is_none();
1324 let had_copy_source = node.copy_source.is_none();
1316 let old = node
1325 let old = node
1317 .copy_source
1326 .copy_source
1318 .replace(value.to_owned().into())
1327 .replace(value.to_owned().into())
1319 .map(Cow::into_owned);
1328 .map(Cow::into_owned);
1320 if had_copy_source {
1329 if had_copy_source {
1321 map.nodes_with_copy_source_count += 1
1330 map.nodes_with_copy_source_count += 1
1322 }
1331 }
1323 Ok(old)
1332 Ok(old)
1324 })
1333 })
1325 }
1334 }
1326
1335
1327 pub fn len(&self) -> usize {
1336 pub fn len(&self) -> usize {
1328 let map = self.get_map();
1337 let map = self.get_map();
1329 map.nodes_with_entry_count as usize
1338 map.nodes_with_entry_count as usize
1330 }
1339 }
1331
1340
1332 pub fn contains_key(
1341 pub fn contains_key(
1333 &self,
1342 &self,
1334 key: &HgPath,
1343 key: &HgPath,
1335 ) -> Result<bool, DirstateV2ParseError> {
1344 ) -> Result<bool, DirstateV2ParseError> {
1336 Ok(self.get(key)?.is_some())
1345 Ok(self.get(key)?.is_some())
1337 }
1346 }
1338
1347
1339 pub fn get(
1348 pub fn get(
1340 &self,
1349 &self,
1341 key: &HgPath,
1350 key: &HgPath,
1342 ) -> Result<Option<DirstateEntry>, DirstateV2ParseError> {
1351 ) -> Result<Option<DirstateEntry>, DirstateV2ParseError> {
1343 let map = self.get_map();
1352 let map = self.get_map();
1344 Ok(if let Some(node) = map.get_node(key)? {
1353 Ok(if let Some(node) = map.get_node(key)? {
1345 node.entry()?
1354 node.entry()?
1346 } else {
1355 } else {
1347 None
1356 None
1348 })
1357 })
1349 }
1358 }
1350
1359
1351 pub fn iter(&self) -> StateMapIter<'_> {
1360 pub fn iter(&self) -> StateMapIter<'_> {
1352 let map = self.get_map();
1361 let map = self.get_map();
1353 Box::new(filter_map_results(map.iter_nodes(), move |node| {
1362 Box::new(filter_map_results(map.iter_nodes(), move |node| {
1354 Ok(if let Some(entry) = node.entry()? {
1363 Ok(if let Some(entry) = node.entry()? {
1355 Some((node.full_path(map.on_disk)?, entry))
1364 Some((node.full_path(map.on_disk)?, entry))
1356 } else {
1365 } else {
1357 None
1366 None
1358 })
1367 })
1359 }))
1368 }))
1360 }
1369 }
1361
1370
1362 pub fn iter_tracked_dirs(
1371 pub fn iter_tracked_dirs(
1363 &mut self,
1372 &mut self,
1364 ) -> Result<
1373 ) -> Result<
1365 Box<
1374 Box<
1366 dyn Iterator<Item = Result<&HgPath, DirstateV2ParseError>>
1375 dyn Iterator<Item = Result<&HgPath, DirstateV2ParseError>>
1367 + Send
1376 + Send
1368 + '_,
1377 + '_,
1369 >,
1378 >,
1370 DirstateError,
1379 DirstateError,
1371 > {
1380 > {
1372 let map = self.get_map();
1381 let map = self.get_map();
1373 let on_disk = map.on_disk;
1382 let on_disk = map.on_disk;
1374 Ok(Box::new(filter_map_results(
1383 Ok(Box::new(filter_map_results(
1375 map.iter_nodes(),
1384 map.iter_nodes(),
1376 move |node| {
1385 move |node| {
1377 Ok(if node.tracked_descendants_count() > 0 {
1386 Ok(if node.tracked_descendants_count() > 0 {
1378 Some(node.full_path(on_disk)?)
1387 Some(node.full_path(on_disk)?)
1379 } else {
1388 } else {
1380 None
1389 None
1381 })
1390 })
1382 },
1391 },
1383 )))
1392 )))
1384 }
1393 }
1385
1394
1386 /// Only public because it needs to be exposed to the Python layer.
1395 /// Only public because it needs to be exposed to the Python layer.
1387 /// It is not the full `setparents` logic, only the parts that mutate the
1396 /// It is not the full `setparents` logic, only the parts that mutate the
1388 /// entries.
1397 /// entries.
1389 pub fn setparents_fixup(
1398 pub fn setparents_fixup(
1390 &mut self,
1399 &mut self,
1391 ) -> Result<Vec<(HgPathBuf, HgPathBuf)>, DirstateV2ParseError> {
1400 ) -> Result<Vec<(HgPathBuf, HgPathBuf)>, DirstateV2ParseError> {
1392 // XXX
1401 // XXX
1393 // All the copying and re-querying is quite inefficient, but this is
1402 // All the copying and re-querying is quite inefficient, but this is
1394 // still a lot better than doing it from Python.
1403 // still a lot better than doing it from Python.
1395 //
1404 //
1396 // The better solution is to develop a mechanism for `iter_mut`,
1405 // The better solution is to develop a mechanism for `iter_mut`,
1397 // which will be a lot more involved: we're dealing with a lazy,
1406 // which will be a lot more involved: we're dealing with a lazy,
1398 // append-mostly, tree-like data structure. This will do for now.
1407 // append-mostly, tree-like data structure. This will do for now.
1399 let mut copies = vec![];
1408 let mut copies = vec![];
1400 let mut files_with_p2_info = vec![];
1409 let mut files_with_p2_info = vec![];
1401 for res in self.iter() {
1410 for res in self.iter() {
1402 let (path, entry) = res?;
1411 let (path, entry) = res?;
1403 if entry.p2_info() {
1412 if entry.p2_info() {
1404 files_with_p2_info.push(path.to_owned())
1413 files_with_p2_info.push(path.to_owned())
1405 }
1414 }
1406 }
1415 }
1407 self.with_dmap_mut(|map| {
1416 self.with_dmap_mut(|map| {
1408 for path in files_with_p2_info.iter() {
1417 for path in files_with_p2_info.iter() {
1409 let node = map.get_or_insert_node(path, |_| {})?;
1418 let node = map.get_or_insert_node(path, |_| {})?;
1410 let entry =
1419 let entry =
1411 node.data.as_entry_mut().expect("entry should exist");
1420 node.data.as_entry_mut().expect("entry should exist");
1412 entry.drop_merge_data();
1421 entry.drop_merge_data();
1413 if let Some(source) = node.copy_source.take().as_deref() {
1422 if let Some(source) = node.copy_source.take().as_deref() {
1414 copies.push((path.to_owned(), source.to_owned()));
1423 copies.push((path.to_owned(), source.to_owned()));
1415 }
1424 }
1416 }
1425 }
1417 Ok(copies)
1426 Ok(copies)
1418 })
1427 })
1419 }
1428 }
1420
1429
1421 pub fn debug_iter(
1430 pub fn debug_iter(
1422 &self,
1431 &self,
1423 all: bool,
1432 all: bool,
1424 ) -> Box<
1433 ) -> Box<
1425 dyn Iterator<
1434 dyn Iterator<
1426 Item = Result<
1435 Item = Result<
1427 (&HgPath, (u8, i32, i32, i32)),
1436 (&HgPath, (u8, i32, i32, i32)),
1428 DirstateV2ParseError,
1437 DirstateV2ParseError,
1429 >,
1438 >,
1430 > + Send
1439 > + Send
1431 + '_,
1440 + '_,
1432 > {
1441 > {
1433 let map = self.get_map();
1442 let map = self.get_map();
1434 Box::new(filter_map_results(map.iter_nodes(), move |node| {
1443 Box::new(filter_map_results(map.iter_nodes(), move |node| {
1435 let debug_tuple = if let Some(entry) = node.entry()? {
1444 let debug_tuple = if let Some(entry) = node.entry()? {
1436 entry.debug_tuple()
1445 entry.debug_tuple()
1437 } else if !all {
1446 } else if !all {
1438 return Ok(None);
1447 return Ok(None);
1439 } else if let Some(mtime) = node.cached_directory_mtime()? {
1448 } else if let Some(mtime) = node.cached_directory_mtime()? {
1440 (b' ', 0, -1, mtime.truncated_seconds() as i32)
1449 (b' ', 0, -1, mtime.truncated_seconds() as i32)
1441 } else {
1450 } else {
1442 (b' ', 0, -1, -1)
1451 (b' ', 0, -1, -1)
1443 };
1452 };
1444 Ok(Some((node.full_path(map.on_disk)?, debug_tuple)))
1453 Ok(Some((node.full_path(map.on_disk)?, debug_tuple)))
1445 }))
1454 }))
1446 }
1455 }
1447 }
1456 }
1448 #[cfg(test)]
1457 #[cfg(test)]
1449 mod tests {
1458 mod tests {
1450 use super::*;
1459 use super::*;
1451
1460
1452 /// Shortcut to return tracked descendants of a path.
1461 /// Shortcut to return tracked descendants of a path.
1453 /// Panics if the path does not exist.
1462 /// Panics if the path does not exist.
1454 fn tracked_descendants(map: &OwningDirstateMap, path: &[u8]) -> u32 {
1463 fn tracked_descendants(map: &OwningDirstateMap, path: &[u8]) -> u32 {
1455 let path = dbg!(HgPath::new(path));
1464 let path = dbg!(HgPath::new(path));
1456 let node = map.get_map().get_node(path);
1465 let node = map.get_map().get_node(path);
1457 node.unwrap().unwrap().tracked_descendants_count()
1466 node.unwrap().unwrap().tracked_descendants_count()
1458 }
1467 }
1459
1468
1460 /// Shortcut to return descendants with an entry.
1469 /// Shortcut to return descendants with an entry.
1461 /// Panics if the path does not exist.
1470 /// Panics if the path does not exist.
1462 fn descendants_with_an_entry(map: &OwningDirstateMap, path: &[u8]) -> u32 {
1471 fn descendants_with_an_entry(map: &OwningDirstateMap, path: &[u8]) -> u32 {
1463 let path = dbg!(HgPath::new(path));
1472 let path = dbg!(HgPath::new(path));
1464 let node = map.get_map().get_node(path);
1473 let node = map.get_map().get_node(path);
1465 node.unwrap().unwrap().descendants_with_entry_count()
1474 node.unwrap().unwrap().descendants_with_entry_count()
1466 }
1475 }
1467
1476
1468 fn assert_does_not_exist(map: &OwningDirstateMap, path: &[u8]) {
1477 fn assert_does_not_exist(map: &OwningDirstateMap, path: &[u8]) {
1469 let path = dbg!(HgPath::new(path));
1478 let path = dbg!(HgPath::new(path));
1470 let node = map.get_map().get_node(path);
1479 let node = map.get_map().get_node(path);
1471 assert!(node.unwrap().is_none());
1480 assert!(node.unwrap().is_none());
1472 }
1481 }
1473
1482
1474 /// Shortcut for path creation in tests
1483 /// Shortcut for path creation in tests
1475 fn p(b: &[u8]) -> &HgPath {
1484 fn p(b: &[u8]) -> &HgPath {
1476 HgPath::new(b)
1485 HgPath::new(b)
1477 }
1486 }
1478
1487
1479 /// Test the very simple case a single tracked file
1488 /// Test the very simple case a single tracked file
1480 #[test]
1489 #[test]
1481 fn test_tracked_descendants_simple() -> Result<(), DirstateError> {
1490 fn test_tracked_descendants_simple() -> Result<(), DirstateError> {
1482 let mut map = OwningDirstateMap::new_empty(vec![]);
1491 let mut map = OwningDirstateMap::new_empty(vec![]);
1483 assert_eq!(map.len(), 0);
1492 assert_eq!(map.len(), 0);
1484
1493
1485 map.set_tracked(p(b"some/nested/path"))?;
1494 map.set_tracked(p(b"some/nested/path"))?;
1486
1495
1487 assert_eq!(map.len(), 1);
1496 assert_eq!(map.len(), 1);
1488 assert_eq!(tracked_descendants(&map, b"some"), 1);
1497 assert_eq!(tracked_descendants(&map, b"some"), 1);
1489 assert_eq!(tracked_descendants(&map, b"some/nested"), 1);
1498 assert_eq!(tracked_descendants(&map, b"some/nested"), 1);
1490 assert_eq!(tracked_descendants(&map, b"some/nested/path"), 0);
1499 assert_eq!(tracked_descendants(&map, b"some/nested/path"), 0);
1491
1500
1492 map.set_untracked(p(b"some/nested/path"))?;
1501 map.set_untracked(p(b"some/nested/path"))?;
1493 assert_eq!(map.len(), 0);
1502 assert_eq!(map.len(), 0);
1494 assert!(map.get_map().get_node(p(b"some"))?.is_none());
1503 assert!(map.get_map().get_node(p(b"some"))?.is_none());
1495
1504
1496 Ok(())
1505 Ok(())
1497 }
1506 }
1498
1507
1499 /// Test the simple case of all tracked, but multiple files
1508 /// Test the simple case of all tracked, but multiple files
1500 #[test]
1509 #[test]
1501 fn test_tracked_descendants_multiple() -> Result<(), DirstateError> {
1510 fn test_tracked_descendants_multiple() -> Result<(), DirstateError> {
1502 let mut map = OwningDirstateMap::new_empty(vec![]);
1511 let mut map = OwningDirstateMap::new_empty(vec![]);
1503
1512
1504 map.set_tracked(p(b"some/nested/path"))?;
1513 map.set_tracked(p(b"some/nested/path"))?;
1505 map.set_tracked(p(b"some/nested/file"))?;
1514 map.set_tracked(p(b"some/nested/file"))?;
1506 // one layer without any files to test deletion cascade
1515 // one layer without any files to test deletion cascade
1507 map.set_tracked(p(b"some/other/nested/path"))?;
1516 map.set_tracked(p(b"some/other/nested/path"))?;
1508 map.set_tracked(p(b"root_file"))?;
1517 map.set_tracked(p(b"root_file"))?;
1509 map.set_tracked(p(b"some/file"))?;
1518 map.set_tracked(p(b"some/file"))?;
1510 map.set_tracked(p(b"some/file2"))?;
1519 map.set_tracked(p(b"some/file2"))?;
1511 map.set_tracked(p(b"some/file3"))?;
1520 map.set_tracked(p(b"some/file3"))?;
1512
1521
1513 assert_eq!(map.len(), 7);
1522 assert_eq!(map.len(), 7);
1514 assert_eq!(tracked_descendants(&map, b"some"), 6);
1523 assert_eq!(tracked_descendants(&map, b"some"), 6);
1515 assert_eq!(tracked_descendants(&map, b"some/nested"), 2);
1524 assert_eq!(tracked_descendants(&map, b"some/nested"), 2);
1516 assert_eq!(tracked_descendants(&map, b"some/other"), 1);
1525 assert_eq!(tracked_descendants(&map, b"some/other"), 1);
1517 assert_eq!(tracked_descendants(&map, b"some/other/nested"), 1);
1526 assert_eq!(tracked_descendants(&map, b"some/other/nested"), 1);
1518 assert_eq!(tracked_descendants(&map, b"some/nested/path"), 0);
1527 assert_eq!(tracked_descendants(&map, b"some/nested/path"), 0);
1519
1528
1520 map.set_untracked(p(b"some/nested/path"))?;
1529 map.set_untracked(p(b"some/nested/path"))?;
1521 assert_eq!(map.len(), 6);
1530 assert_eq!(map.len(), 6);
1522 assert_eq!(tracked_descendants(&map, b"some"), 5);
1531 assert_eq!(tracked_descendants(&map, b"some"), 5);
1523 assert_eq!(tracked_descendants(&map, b"some/nested"), 1);
1532 assert_eq!(tracked_descendants(&map, b"some/nested"), 1);
1524 assert_eq!(tracked_descendants(&map, b"some/other"), 1);
1533 assert_eq!(tracked_descendants(&map, b"some/other"), 1);
1525 assert_eq!(tracked_descendants(&map, b"some/other/nested"), 1);
1534 assert_eq!(tracked_descendants(&map, b"some/other/nested"), 1);
1526
1535
1527 map.set_untracked(p(b"some/nested/file"))?;
1536 map.set_untracked(p(b"some/nested/file"))?;
1528 assert_eq!(map.len(), 5);
1537 assert_eq!(map.len(), 5);
1529 assert_eq!(tracked_descendants(&map, b"some"), 4);
1538 assert_eq!(tracked_descendants(&map, b"some"), 4);
1530 assert_eq!(tracked_descendants(&map, b"some/other"), 1);
1539 assert_eq!(tracked_descendants(&map, b"some/other"), 1);
1531 assert_eq!(tracked_descendants(&map, b"some/other/nested"), 1);
1540 assert_eq!(tracked_descendants(&map, b"some/other/nested"), 1);
1532 assert_does_not_exist(&map, b"some_nested");
1541 assert_does_not_exist(&map, b"some_nested");
1533
1542
1534 map.set_untracked(p(b"some/other/nested/path"))?;
1543 map.set_untracked(p(b"some/other/nested/path"))?;
1535 assert_eq!(map.len(), 4);
1544 assert_eq!(map.len(), 4);
1536 assert_eq!(tracked_descendants(&map, b"some"), 3);
1545 assert_eq!(tracked_descendants(&map, b"some"), 3);
1537 assert_does_not_exist(&map, b"some/other");
1546 assert_does_not_exist(&map, b"some/other");
1538
1547
1539 map.set_untracked(p(b"root_file"))?;
1548 map.set_untracked(p(b"root_file"))?;
1540 assert_eq!(map.len(), 3);
1549 assert_eq!(map.len(), 3);
1541 assert_eq!(tracked_descendants(&map, b"some"), 3);
1550 assert_eq!(tracked_descendants(&map, b"some"), 3);
1542 assert_does_not_exist(&map, b"root_file");
1551 assert_does_not_exist(&map, b"root_file");
1543
1552
1544 map.set_untracked(p(b"some/file"))?;
1553 map.set_untracked(p(b"some/file"))?;
1545 assert_eq!(map.len(), 2);
1554 assert_eq!(map.len(), 2);
1546 assert_eq!(tracked_descendants(&map, b"some"), 2);
1555 assert_eq!(tracked_descendants(&map, b"some"), 2);
1547 assert_does_not_exist(&map, b"some/file");
1556 assert_does_not_exist(&map, b"some/file");
1548
1557
1549 map.set_untracked(p(b"some/file2"))?;
1558 map.set_untracked(p(b"some/file2"))?;
1550 assert_eq!(map.len(), 1);
1559 assert_eq!(map.len(), 1);
1551 assert_eq!(tracked_descendants(&map, b"some"), 1);
1560 assert_eq!(tracked_descendants(&map, b"some"), 1);
1552 assert_does_not_exist(&map, b"some/file2");
1561 assert_does_not_exist(&map, b"some/file2");
1553
1562
1554 map.set_untracked(p(b"some/file3"))?;
1563 map.set_untracked(p(b"some/file3"))?;
1555 assert_eq!(map.len(), 0);
1564 assert_eq!(map.len(), 0);
1556 assert_does_not_exist(&map, b"some/file3");
1565 assert_does_not_exist(&map, b"some/file3");
1557
1566
1558 Ok(())
1567 Ok(())
1559 }
1568 }
1560
1569
1561 /// Check with a mix of tracked and non-tracked items
1570 /// Check with a mix of tracked and non-tracked items
1562 #[test]
1571 #[test]
1563 fn test_tracked_descendants_different() -> Result<(), DirstateError> {
1572 fn test_tracked_descendants_different() -> Result<(), DirstateError> {
1564 let mut map = OwningDirstateMap::new_empty(vec![]);
1573 let mut map = OwningDirstateMap::new_empty(vec![]);
1565
1574
1566 // A file that was just added
1575 // A file that was just added
1567 map.set_tracked(p(b"some/nested/path"))?;
1576 map.set_tracked(p(b"some/nested/path"))?;
1568 // This has no information, the dirstate should ignore it
1577 // This has no information, the dirstate should ignore it
1569 map.reset_state(p(b"some/file"), false, false, false, false, None)?;
1578 map.reset_state(p(b"some/file"), false, false, false, false, None)?;
1570 assert_does_not_exist(&map, b"some/file");
1579 assert_does_not_exist(&map, b"some/file");
1571
1580
1572 // A file that was removed
1581 // A file that was removed
1573 map.reset_state(
1582 map.reset_state(
1574 p(b"some/nested/file"),
1583 p(b"some/nested/file"),
1575 false,
1584 false,
1576 true,
1585 true,
1577 false,
1586 false,
1578 false,
1587 false,
1579 None,
1588 None,
1580 )?;
1589 )?;
1581 assert!(!map.get(p(b"some/nested/file"))?.unwrap().tracked());
1590 assert!(!map.get(p(b"some/nested/file"))?.unwrap().tracked());
1582 // Only present in p2
1591 // Only present in p2
1583 map.reset_state(p(b"some/file3"), false, false, true, false, None)?;
1592 map.reset_state(p(b"some/file3"), false, false, true, false, None)?;
1584 assert!(!map.get(p(b"some/file3"))?.unwrap().tracked());
1593 assert!(!map.get(p(b"some/file3"))?.unwrap().tracked());
1585 // A file that was merged
1594 // A file that was merged
1586 map.reset_state(p(b"root_file"), true, true, true, false, None)?;
1595 map.reset_state(p(b"root_file"), true, true, true, false, None)?;
1587 assert!(map.get(p(b"root_file"))?.unwrap().tracked());
1596 assert!(map.get(p(b"root_file"))?.unwrap().tracked());
1588 // A file that is added, with info from p2
1597 // A file that is added, with info from p2
1589 // XXX is that actually possible?
1598 // XXX is that actually possible?
1590 map.reset_state(p(b"some/file2"), true, false, true, false, None)?;
1599 map.reset_state(p(b"some/file2"), true, false, true, false, None)?;
1591 assert!(map.get(p(b"some/file2"))?.unwrap().tracked());
1600 assert!(map.get(p(b"some/file2"))?.unwrap().tracked());
1592 // A clean file
1601 // A clean file
1593 // One layer without any files to test deletion cascade
1602 // One layer without any files to test deletion cascade
1594 map.reset_state(
1603 map.reset_state(
1595 p(b"some/other/nested/path"),
1604 p(b"some/other/nested/path"),
1596 true,
1605 true,
1597 true,
1606 true,
1598 false,
1607 false,
1599 false,
1608 false,
1600 None,
1609 None,
1601 )?;
1610 )?;
1602 assert!(map.get(p(b"some/other/nested/path"))?.unwrap().tracked());
1611 assert!(map.get(p(b"some/other/nested/path"))?.unwrap().tracked());
1603
1612
1604 assert_eq!(map.len(), 6);
1613 assert_eq!(map.len(), 6);
1605 assert_eq!(tracked_descendants(&map, b"some"), 3);
1614 assert_eq!(tracked_descendants(&map, b"some"), 3);
1606 assert_eq!(descendants_with_an_entry(&map, b"some"), 5);
1615 assert_eq!(descendants_with_an_entry(&map, b"some"), 5);
1607 assert_eq!(tracked_descendants(&map, b"some/other/nested"), 1);
1616 assert_eq!(tracked_descendants(&map, b"some/other/nested"), 1);
1608 assert_eq!(descendants_with_an_entry(&map, b"some/other/nested"), 1);
1617 assert_eq!(descendants_with_an_entry(&map, b"some/other/nested"), 1);
1609 assert_eq!(tracked_descendants(&map, b"some/other/nested/path"), 0);
1618 assert_eq!(tracked_descendants(&map, b"some/other/nested/path"), 0);
1610 assert_eq!(
1619 assert_eq!(
1611 descendants_with_an_entry(&map, b"some/other/nested/path"),
1620 descendants_with_an_entry(&map, b"some/other/nested/path"),
1612 0
1621 0
1613 );
1622 );
1614 assert_eq!(tracked_descendants(&map, b"some/nested"), 1);
1623 assert_eq!(tracked_descendants(&map, b"some/nested"), 1);
1615 assert_eq!(descendants_with_an_entry(&map, b"some/nested"), 2);
1624 assert_eq!(descendants_with_an_entry(&map, b"some/nested"), 2);
1616
1625
1617 // might as well check this
1626 // might as well check this
1618 map.set_untracked(p(b"path/does/not/exist"))?;
1627 map.set_untracked(p(b"path/does/not/exist"))?;
1619 assert_eq!(map.len(), 6);
1628 assert_eq!(map.len(), 6);
1620
1629
1621 map.set_untracked(p(b"some/other/nested/path"))?;
1630 map.set_untracked(p(b"some/other/nested/path"))?;
1622 // It is set untracked but not deleted since it held other information
1631 // It is set untracked but not deleted since it held other information
1623 assert_eq!(map.len(), 6);
1632 assert_eq!(map.len(), 6);
1624 assert_eq!(tracked_descendants(&map, b"some"), 2);
1633 assert_eq!(tracked_descendants(&map, b"some"), 2);
1625 assert_eq!(descendants_with_an_entry(&map, b"some"), 5);
1634 assert_eq!(descendants_with_an_entry(&map, b"some"), 5);
1626 assert_eq!(descendants_with_an_entry(&map, b"some/other"), 1);
1635 assert_eq!(descendants_with_an_entry(&map, b"some/other"), 1);
1627 assert_eq!(descendants_with_an_entry(&map, b"some/other/nested"), 1);
1636 assert_eq!(descendants_with_an_entry(&map, b"some/other/nested"), 1);
1628 assert_eq!(tracked_descendants(&map, b"some/nested"), 1);
1637 assert_eq!(tracked_descendants(&map, b"some/nested"), 1);
1629 assert_eq!(descendants_with_an_entry(&map, b"some/nested"), 2);
1638 assert_eq!(descendants_with_an_entry(&map, b"some/nested"), 2);
1630
1639
1631 map.set_untracked(p(b"some/nested/path"))?;
1640 map.set_untracked(p(b"some/nested/path"))?;
1632 // It is set untracked *and* deleted since it was only added
1641 // It is set untracked *and* deleted since it was only added
1633 assert_eq!(map.len(), 5);
1642 assert_eq!(map.len(), 5);
1634 assert_eq!(tracked_descendants(&map, b"some"), 1);
1643 assert_eq!(tracked_descendants(&map, b"some"), 1);
1635 assert_eq!(descendants_with_an_entry(&map, b"some"), 4);
1644 assert_eq!(descendants_with_an_entry(&map, b"some"), 4);
1636 assert_eq!(tracked_descendants(&map, b"some/nested"), 0);
1645 assert_eq!(tracked_descendants(&map, b"some/nested"), 0);
1637 assert_eq!(descendants_with_an_entry(&map, b"some/nested"), 1);
1646 assert_eq!(descendants_with_an_entry(&map, b"some/nested"), 1);
1638 assert_does_not_exist(&map, b"some/nested/path");
1647 assert_does_not_exist(&map, b"some/nested/path");
1639
1648
1640 map.set_untracked(p(b"root_file"))?;
1649 map.set_untracked(p(b"root_file"))?;
1641 // Untracked but not deleted
1650 // Untracked but not deleted
1642 assert_eq!(map.len(), 5);
1651 assert_eq!(map.len(), 5);
1643 assert!(map.get(p(b"root_file"))?.is_some());
1652 assert!(map.get(p(b"root_file"))?.is_some());
1644
1653
1645 map.set_untracked(p(b"some/file2"))?;
1654 map.set_untracked(p(b"some/file2"))?;
1646 assert_eq!(map.len(), 5);
1655 assert_eq!(map.len(), 5);
1647 assert_eq!(tracked_descendants(&map, b"some"), 0);
1656 assert_eq!(tracked_descendants(&map, b"some"), 0);
1648 assert!(map.get(p(b"some/file2"))?.is_some());
1657 assert!(map.get(p(b"some/file2"))?.is_some());
1649
1658
1650 map.set_untracked(p(b"some/file3"))?;
1659 map.set_untracked(p(b"some/file3"))?;
1651 assert_eq!(map.len(), 5);
1660 assert_eq!(map.len(), 5);
1652 assert_eq!(tracked_descendants(&map, b"some"), 0);
1661 assert_eq!(tracked_descendants(&map, b"some"), 0);
1653 assert!(map.get(p(b"some/file3"))?.is_some());
1662 assert!(map.get(p(b"some/file3"))?.is_some());
1654
1663
1655 Ok(())
1664 Ok(())
1656 }
1665 }
1657
1666
1658 /// Check that copies counter is correctly updated
1667 /// Check that copies counter is correctly updated
1659 #[test]
1668 #[test]
1660 fn test_copy_source() -> Result<(), DirstateError> {
1669 fn test_copy_source() -> Result<(), DirstateError> {
1661 let mut map = OwningDirstateMap::new_empty(vec![]);
1670 let mut map = OwningDirstateMap::new_empty(vec![]);
1662
1671
1663 // Clean file
1672 // Clean file
1664 map.reset_state(p(b"files/clean"), true, true, false, false, None)?;
1673 map.reset_state(p(b"files/clean"), true, true, false, false, None)?;
1665 // Merged file
1674 // Merged file
1666 map.reset_state(p(b"files/from_p2"), true, true, true, false, None)?;
1675 map.reset_state(p(b"files/from_p2"), true, true, true, false, None)?;
1667 // Removed file
1676 // Removed file
1668 map.reset_state(p(b"removed"), false, true, false, false, None)?;
1677 map.reset_state(p(b"removed"), false, true, false, false, None)?;
1669 // Added file
1678 // Added file
1670 map.reset_state(p(b"files/added"), true, false, false, false, None)?;
1679 map.reset_state(p(b"files/added"), true, false, false, false, None)?;
1671 // Add copy
1680 // Add copy
1672 map.copy_map_insert(p(b"files/clean"), p(b"clean_copy_source"))?;
1681 map.copy_map_insert(p(b"files/clean"), p(b"clean_copy_source"))?;
1673 assert_eq!(map.copy_map_len(), 1);
1682 assert_eq!(map.copy_map_len(), 1);
1674
1683
1675 // Copy override
1684 // Copy override
1676 map.copy_map_insert(p(b"files/clean"), p(b"other_clean_copy_source"))?;
1685 map.copy_map_insert(p(b"files/clean"), p(b"other_clean_copy_source"))?;
1677 assert_eq!(map.copy_map_len(), 1);
1686 assert_eq!(map.copy_map_len(), 1);
1678
1687
1679 // Multiple copies
1688 // Multiple copies
1680 map.copy_map_insert(p(b"removed"), p(b"removed_copy_source"))?;
1689 map.copy_map_insert(p(b"removed"), p(b"removed_copy_source"))?;
1681 assert_eq!(map.copy_map_len(), 2);
1690 assert_eq!(map.copy_map_len(), 2);
1682
1691
1683 map.copy_map_insert(p(b"files/added"), p(b"added_copy_source"))?;
1692 map.copy_map_insert(p(b"files/added"), p(b"added_copy_source"))?;
1684 assert_eq!(map.copy_map_len(), 3);
1693 assert_eq!(map.copy_map_len(), 3);
1685
1694
1686 // Added, so the entry is completely removed
1695 // Added, so the entry is completely removed
1687 map.set_untracked(p(b"files/added"))?;
1696 map.set_untracked(p(b"files/added"))?;
1688 assert_does_not_exist(&map, b"files/added");
1697 assert_does_not_exist(&map, b"files/added");
1689 assert_eq!(map.copy_map_len(), 2);
1698 assert_eq!(map.copy_map_len(), 2);
1690
1699
1691 // Removed, so the entry is kept around, so is its copy
1700 // Removed, so the entry is kept around, so is its copy
1692 map.set_untracked(p(b"removed"))?;
1701 map.set_untracked(p(b"removed"))?;
1693 assert!(map.get(p(b"removed"))?.is_some());
1702 assert!(map.get(p(b"removed"))?.is_some());
1694 assert_eq!(map.copy_map_len(), 2);
1703 assert_eq!(map.copy_map_len(), 2);
1695
1704
1696 // Clean, so the entry is kept around, but not its copy
1705 // Clean, so the entry is kept around, but not its copy
1697 map.set_untracked(p(b"files/clean"))?;
1706 map.set_untracked(p(b"files/clean"))?;
1698 assert!(map.get(p(b"files/clean"))?.is_some());
1707 assert!(map.get(p(b"files/clean"))?.is_some());
1699 assert_eq!(map.copy_map_len(), 1);
1708 assert_eq!(map.copy_map_len(), 1);
1700
1709
1701 map.copy_map_insert(p(b"files/from_p2"), p(b"from_p2_copy_source"))?;
1710 map.copy_map_insert(p(b"files/from_p2"), p(b"from_p2_copy_source"))?;
1702 assert_eq!(map.copy_map_len(), 2);
1711 assert_eq!(map.copy_map_len(), 2);
1703
1712
1704 // Info from p2, so its copy source info is kept around
1713 // Info from p2, so its copy source info is kept around
1705 map.set_untracked(p(b"files/from_p2"))?;
1714 map.set_untracked(p(b"files/from_p2"))?;
1706 assert!(map.get(p(b"files/from_p2"))?.is_some());
1715 assert!(map.get(p(b"files/from_p2"))?.is_some());
1707 assert_eq!(map.copy_map_len(), 2);
1716 assert_eq!(map.copy_map_len(), 2);
1708
1717
1709 Ok(())
1718 Ok(())
1710 }
1719 }
1711
1720
1712 /// Test with "on disk" data. For the sake of this test, the "on disk" data
1721 /// Test with "on disk" data. For the sake of this test, the "on disk" data
1713 /// does not actually come from the disk, but it's opaque to the code being
1722 /// does not actually come from the disk, but it's opaque to the code being
1714 /// tested.
1723 /// tested.
1715 #[test]
1724 #[test]
1716 fn test_on_disk() -> Result<(), DirstateError> {
1725 fn test_on_disk() -> Result<(), DirstateError> {
1717 // First let's create some data to put "on disk"
1726 // First let's create some data to put "on disk"
1718 let mut map = OwningDirstateMap::new_empty(vec![]);
1727 let mut map = OwningDirstateMap::new_empty(vec![]);
1719
1728
1720 // A file that was just added
1729 // A file that was just added
1721 map.set_tracked(p(b"some/nested/added"))?;
1730 map.set_tracked(p(b"some/nested/added"))?;
1722 map.copy_map_insert(p(b"some/nested/added"), p(b"added_copy_source"))?;
1731 map.copy_map_insert(p(b"some/nested/added"), p(b"added_copy_source"))?;
1723
1732
1724 // A file that was removed
1733 // A file that was removed
1725 map.reset_state(
1734 map.reset_state(
1726 p(b"some/nested/removed"),
1735 p(b"some/nested/removed"),
1727 false,
1736 false,
1728 true,
1737 true,
1729 false,
1738 false,
1730 false,
1739 false,
1731 None,
1740 None,
1732 )?;
1741 )?;
1733 // Only present in p2
1742 // Only present in p2
1734 map.reset_state(
1743 map.reset_state(
1735 p(b"other/p2_info_only"),
1744 p(b"other/p2_info_only"),
1736 false,
1745 false,
1737 false,
1746 false,
1738 true,
1747 true,
1739 false,
1748 false,
1740 None,
1749 None,
1741 )?;
1750 )?;
1742 map.copy_map_insert(
1751 map.copy_map_insert(
1743 p(b"other/p2_info_only"),
1752 p(b"other/p2_info_only"),
1744 p(b"other/p2_info_copy_source"),
1753 p(b"other/p2_info_copy_source"),
1745 )?;
1754 )?;
1746 // A file that was merged
1755 // A file that was merged
1747 map.reset_state(p(b"merged"), true, true, true, false, None)?;
1756 map.reset_state(p(b"merged"), true, true, true, false, None)?;
1748 // A file that is added, with info from p2
1757 // A file that is added, with info from p2
1749 // XXX is that actually possible?
1758 // XXX is that actually possible?
1750 map.reset_state(
1759 map.reset_state(
1751 p(b"other/added_with_p2"),
1760 p(b"other/added_with_p2"),
1752 true,
1761 true,
1753 false,
1762 false,
1754 true,
1763 true,
1755 false,
1764 false,
1756 None,
1765 None,
1757 )?;
1766 )?;
1758 // One layer without any files to test deletion cascade
1767 // One layer without any files to test deletion cascade
1759 // A clean file
1768 // A clean file
1760 map.reset_state(
1769 map.reset_state(
1761 p(b"some/other/nested/clean"),
1770 p(b"some/other/nested/clean"),
1762 true,
1771 true,
1763 true,
1772 true,
1764 false,
1773 false,
1765 false,
1774 false,
1766 None,
1775 None,
1767 )?;
1776 )?;
1768
1777
1769 let (packed, metadata, _should_append) = map.pack_v2(false)?;
1778 let (packed, metadata, _should_append) = map.pack_v2(false)?;
1770 let packed_len = packed.len();
1779 let packed_len = packed.len();
1771 assert!(packed_len > 0);
1780 assert!(packed_len > 0);
1772
1781
1773 // Recreate "from disk"
1782 // Recreate "from disk"
1774 let mut map = OwningDirstateMap::new_v2(
1783 let mut map = OwningDirstateMap::new_v2(
1775 packed,
1784 packed,
1776 packed_len,
1785 packed_len,
1777 metadata.as_bytes(),
1786 metadata.as_bytes(),
1778 )?;
1787 )?;
1779
1788
1780 // Check that everything is accounted for
1789 // Check that everything is accounted for
1781 assert!(map.contains_key(p(b"some/nested/added"))?);
1790 assert!(map.contains_key(p(b"some/nested/added"))?);
1782 assert!(map.contains_key(p(b"some/nested/removed"))?);
1791 assert!(map.contains_key(p(b"some/nested/removed"))?);
1783 assert!(map.contains_key(p(b"merged"))?);
1792 assert!(map.contains_key(p(b"merged"))?);
1784 assert!(map.contains_key(p(b"other/p2_info_only"))?);
1793 assert!(map.contains_key(p(b"other/p2_info_only"))?);
1785 assert!(map.contains_key(p(b"other/added_with_p2"))?);
1794 assert!(map.contains_key(p(b"other/added_with_p2"))?);
1786 assert!(map.contains_key(p(b"some/other/nested/clean"))?);
1795 assert!(map.contains_key(p(b"some/other/nested/clean"))?);
1787 assert_eq!(
1796 assert_eq!(
1788 map.copy_map_get(p(b"some/nested/added"))?,
1797 map.copy_map_get(p(b"some/nested/added"))?,
1789 Some(p(b"added_copy_source"))
1798 Some(p(b"added_copy_source"))
1790 );
1799 );
1791 assert_eq!(
1800 assert_eq!(
1792 map.copy_map_get(p(b"other/p2_info_only"))?,
1801 map.copy_map_get(p(b"other/p2_info_only"))?,
1793 Some(p(b"other/p2_info_copy_source"))
1802 Some(p(b"other/p2_info_copy_source"))
1794 );
1803 );
1795 assert_eq!(tracked_descendants(&map, b"some"), 2);
1804 assert_eq!(tracked_descendants(&map, b"some"), 2);
1796 assert_eq!(descendants_with_an_entry(&map, b"some"), 3);
1805 assert_eq!(descendants_with_an_entry(&map, b"some"), 3);
1797 assert_eq!(tracked_descendants(&map, b"other"), 1);
1806 assert_eq!(tracked_descendants(&map, b"other"), 1);
1798 assert_eq!(descendants_with_an_entry(&map, b"other"), 2);
1807 assert_eq!(descendants_with_an_entry(&map, b"other"), 2);
1799 assert_eq!(tracked_descendants(&map, b"some/other"), 1);
1808 assert_eq!(tracked_descendants(&map, b"some/other"), 1);
1800 assert_eq!(descendants_with_an_entry(&map, b"some/other"), 1);
1809 assert_eq!(descendants_with_an_entry(&map, b"some/other"), 1);
1801 assert_eq!(tracked_descendants(&map, b"some/other/nested"), 1);
1810 assert_eq!(tracked_descendants(&map, b"some/other/nested"), 1);
1802 assert_eq!(descendants_with_an_entry(&map, b"some/other/nested"), 1);
1811 assert_eq!(descendants_with_an_entry(&map, b"some/other/nested"), 1);
1803 assert_eq!(tracked_descendants(&map, b"some/nested"), 1);
1812 assert_eq!(tracked_descendants(&map, b"some/nested"), 1);
1804 assert_eq!(descendants_with_an_entry(&map, b"some/nested"), 2);
1813 assert_eq!(descendants_with_an_entry(&map, b"some/nested"), 2);
1805 assert_eq!(map.len(), 6);
1814 assert_eq!(map.len(), 6);
1806 assert_eq!(map.get_map().unreachable_bytes, 0);
1815 assert_eq!(map.get_map().unreachable_bytes, 0);
1807 assert_eq!(map.copy_map_len(), 2);
1816 assert_eq!(map.copy_map_len(), 2);
1808
1817
1809 // Shouldn't change anything since it's already not tracked
1818 // Shouldn't change anything since it's already not tracked
1810 map.set_untracked(p(b"some/nested/removed"))?;
1819 map.set_untracked(p(b"some/nested/removed"))?;
1811 assert_eq!(map.get_map().unreachable_bytes, 0);
1820 assert_eq!(map.get_map().unreachable_bytes, 0);
1812
1821
1813 match map.get_map().root {
1822 match map.get_map().root {
1814 ChildNodes::InMemory(_) => {
1823 ChildNodes::InMemory(_) => {
1815 panic!("root should not have been mutated")
1824 panic!("root should not have been mutated")
1816 }
1825 }
1817 _ => (),
1826 _ => (),
1818 }
1827 }
1819 // We haven't mutated enough (nothing, actually), we should still be in
1828 // We haven't mutated enough (nothing, actually), we should still be in
1820 // the append strategy
1829 // the append strategy
1821 assert!(map.get_map().write_should_append());
1830 assert!(map.get_map().write_should_append());
1822
1831
1823 // But this mutates the structure, so there should be unreachable_bytes
1832 // But this mutates the structure, so there should be unreachable_bytes
1824 assert!(map.set_untracked(p(b"some/nested/added"))?);
1833 assert!(map.set_untracked(p(b"some/nested/added"))?);
1825 let unreachable_bytes = map.get_map().unreachable_bytes;
1834 let unreachable_bytes = map.get_map().unreachable_bytes;
1826 assert!(unreachable_bytes > 0);
1835 assert!(unreachable_bytes > 0);
1827
1836
1828 match map.get_map().root {
1837 match map.get_map().root {
1829 ChildNodes::OnDisk(_) => panic!("root should have been mutated"),
1838 ChildNodes::OnDisk(_) => panic!("root should have been mutated"),
1830 _ => (),
1839 _ => (),
1831 }
1840 }
1832
1841
1833 // This should not mutate the structure either, since `root` has
1842 // This should not mutate the structure either, since `root` has
1834 // already been mutated along with its direct children.
1843 // already been mutated along with its direct children.
1835 map.set_untracked(p(b"merged"))?;
1844 map.set_untracked(p(b"merged"))?;
1836 assert_eq!(map.get_map().unreachable_bytes, unreachable_bytes);
1845 assert_eq!(map.get_map().unreachable_bytes, unreachable_bytes);
1837
1846
1838 match map.get_map().get_node(p(b"other/added_with_p2"))?.unwrap() {
1847 match map.get_map().get_node(p(b"other/added_with_p2"))?.unwrap() {
1839 NodeRef::InMemory(_, _) => {
1848 NodeRef::InMemory(_, _) => {
1840 panic!("'other/added_with_p2' should not have been mutated")
1849 panic!("'other/added_with_p2' should not have been mutated")
1841 }
1850 }
1842 _ => (),
1851 _ => (),
1843 }
1852 }
1844 // But this should, since it's in a different path
1853 // But this should, since it's in a different path
1845 // than `<root>some/nested/add`
1854 // than `<root>some/nested/add`
1846 map.set_untracked(p(b"other/added_with_p2"))?;
1855 map.set_untracked(p(b"other/added_with_p2"))?;
1847 assert!(map.get_map().unreachable_bytes > unreachable_bytes);
1856 assert!(map.get_map().unreachable_bytes > unreachable_bytes);
1848
1857
1849 match map.get_map().get_node(p(b"other/added_with_p2"))?.unwrap() {
1858 match map.get_map().get_node(p(b"other/added_with_p2"))?.unwrap() {
1850 NodeRef::OnDisk(_) => {
1859 NodeRef::OnDisk(_) => {
1851 panic!("'other/added_with_p2' should have been mutated")
1860 panic!("'other/added_with_p2' should have been mutated")
1852 }
1861 }
1853 _ => (),
1862 _ => (),
1854 }
1863 }
1855
1864
1856 // We have rewritten most of the tree, we should create a new file
1865 // We have rewritten most of the tree, we should create a new file
1857 assert!(!map.get_map().write_should_append());
1866 assert!(!map.get_map().write_should_append());
1858
1867
1859 Ok(())
1868 Ok(())
1860 }
1869 }
1861 }
1870 }
General Comments 0
You need to be logged in to leave comments. Login now