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