##// END OF EJS Templates
rust-clippy: simplify return type of debug function...
Raphaël Gomès -
r50819:66ffe374 default
parent child Browse files
Show More
@@ -1,1917 +1,1915 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 type DebugDirstateTuple<'a> = (&'a HgPath, (u8, i32, i32, i32));
943
942 impl OwningDirstateMap {
944 impl OwningDirstateMap {
943 pub fn clear(&mut self) {
945 pub fn clear(&mut self) {
944 self.with_dmap_mut(|map| {
946 self.with_dmap_mut(|map| {
945 map.root = Default::default();
947 map.root = Default::default();
946 map.nodes_with_entry_count = 0;
948 map.nodes_with_entry_count = 0;
947 map.nodes_with_copy_source_count = 0;
949 map.nodes_with_copy_source_count = 0;
948 });
950 });
949 }
951 }
950
952
951 pub fn set_tracked(
953 pub fn set_tracked(
952 &mut self,
954 &mut self,
953 filename: &HgPath,
955 filename: &HgPath,
954 ) -> Result<bool, DirstateV2ParseError> {
956 ) -> Result<bool, DirstateV2ParseError> {
955 let old_entry_opt = self.get(filename)?;
957 let old_entry_opt = self.get(filename)?;
956 self.with_dmap_mut(|map| map.set_tracked(filename, old_entry_opt))
958 self.with_dmap_mut(|map| map.set_tracked(filename, old_entry_opt))
957 }
959 }
958
960
959 pub fn set_untracked(
961 pub fn set_untracked(
960 &mut self,
962 &mut self,
961 filename: &HgPath,
963 filename: &HgPath,
962 ) -> Result<bool, DirstateError> {
964 ) -> Result<bool, DirstateError> {
963 let old_entry_opt = self.get(filename)?;
965 let old_entry_opt = self.get(filename)?;
964 match old_entry_opt {
966 match old_entry_opt {
965 None => Ok(false),
967 None => Ok(false),
966 Some(old_entry) => {
968 Some(old_entry) => {
967 if !old_entry.tracked() {
969 if !old_entry.tracked() {
968 // `DirstateMap::set_untracked` is not a noop if
970 // `DirstateMap::set_untracked` is not a noop if
969 // already not tracked as it will decrement the
971 // already not tracked as it will decrement the
970 // tracked counters while going down.
972 // tracked counters while going down.
971 return Ok(true);
973 return Ok(true);
972 }
974 }
973 if old_entry.added() {
975 if old_entry.added() {
974 // Untracking an "added" entry will just result in a
976 // Untracking an "added" entry will just result in a
975 // worthless entry (and other parts of the code will
977 // worthless entry (and other parts of the code will
976 // complain about it), just drop it entirely.
978 // complain about it), just drop it entirely.
977 self.drop_entry_and_copy_source(filename)?;
979 self.drop_entry_and_copy_source(filename)?;
978 return Ok(true);
980 return Ok(true);
979 }
981 }
980 if !old_entry.p2_info() {
982 if !old_entry.p2_info() {
981 self.copy_map_remove(filename)?;
983 self.copy_map_remove(filename)?;
982 }
984 }
983
985
984 self.with_dmap_mut(|map| {
986 self.with_dmap_mut(|map| {
985 map.set_untracked(filename, old_entry)?;
987 map.set_untracked(filename, old_entry)?;
986 Ok(true)
988 Ok(true)
987 })
989 })
988 }
990 }
989 }
991 }
990 }
992 }
991
993
992 pub fn set_clean(
994 pub fn set_clean(
993 &mut self,
995 &mut self,
994 filename: &HgPath,
996 filename: &HgPath,
995 mode: u32,
997 mode: u32,
996 size: u32,
998 size: u32,
997 mtime: TruncatedTimestamp,
999 mtime: TruncatedTimestamp,
998 ) -> Result<(), DirstateError> {
1000 ) -> Result<(), DirstateError> {
999 let old_entry = match self.get(filename)? {
1001 let old_entry = match self.get(filename)? {
1000 None => {
1002 None => {
1001 return Err(
1003 return Err(
1002 DirstateMapError::PathNotFound(filename.into()).into()
1004 DirstateMapError::PathNotFound(filename.into()).into()
1003 )
1005 )
1004 }
1006 }
1005 Some(e) => e,
1007 Some(e) => e,
1006 };
1008 };
1007 self.copy_map_remove(filename)?;
1009 self.copy_map_remove(filename)?;
1008 self.with_dmap_mut(|map| {
1010 self.with_dmap_mut(|map| {
1009 map.set_clean(filename, old_entry, mode, size, mtime)
1011 map.set_clean(filename, old_entry, mode, size, mtime)
1010 })
1012 })
1011 }
1013 }
1012
1014
1013 pub fn set_possibly_dirty(
1015 pub fn set_possibly_dirty(
1014 &mut self,
1016 &mut self,
1015 filename: &HgPath,
1017 filename: &HgPath,
1016 ) -> Result<(), DirstateError> {
1018 ) -> Result<(), DirstateError> {
1017 if self.get(filename)?.is_none() {
1019 if self.get(filename)?.is_none() {
1018 return Err(DirstateMapError::PathNotFound(filename.into()).into());
1020 return Err(DirstateMapError::PathNotFound(filename.into()).into());
1019 }
1021 }
1020 self.with_dmap_mut(|map| map.set_possibly_dirty(filename))
1022 self.with_dmap_mut(|map| map.set_possibly_dirty(filename))
1021 }
1023 }
1022
1024
1023 pub fn reset_state(
1025 pub fn reset_state(
1024 &mut self,
1026 &mut self,
1025 filename: &HgPath,
1027 filename: &HgPath,
1026 wc_tracked: bool,
1028 wc_tracked: bool,
1027 p1_tracked: bool,
1029 p1_tracked: bool,
1028 p2_info: bool,
1030 p2_info: bool,
1029 has_meaningful_mtime: bool,
1031 has_meaningful_mtime: bool,
1030 parent_file_data_opt: Option<ParentFileData>,
1032 parent_file_data_opt: Option<ParentFileData>,
1031 ) -> Result<(), DirstateError> {
1033 ) -> Result<(), DirstateError> {
1032 if !(p1_tracked || p2_info || wc_tracked) {
1034 if !(p1_tracked || p2_info || wc_tracked) {
1033 self.drop_entry_and_copy_source(filename)?;
1035 self.drop_entry_and_copy_source(filename)?;
1034 return Ok(());
1036 return Ok(());
1035 }
1037 }
1036 self.copy_map_remove(filename)?;
1038 self.copy_map_remove(filename)?;
1037 let old_entry_opt = self.get(filename)?;
1039 let old_entry_opt = self.get(filename)?;
1038 self.with_dmap_mut(|map| {
1040 self.with_dmap_mut(|map| {
1039 map.reset_state(
1041 map.reset_state(
1040 filename,
1042 filename,
1041 old_entry_opt,
1043 old_entry_opt,
1042 wc_tracked,
1044 wc_tracked,
1043 p1_tracked,
1045 p1_tracked,
1044 p2_info,
1046 p2_info,
1045 has_meaningful_mtime,
1047 has_meaningful_mtime,
1046 parent_file_data_opt,
1048 parent_file_data_opt,
1047 )
1049 )
1048 })
1050 })
1049 }
1051 }
1050
1052
1051 pub fn drop_entry_and_copy_source(
1053 pub fn drop_entry_and_copy_source(
1052 &mut self,
1054 &mut self,
1053 filename: &HgPath,
1055 filename: &HgPath,
1054 ) -> Result<(), DirstateError> {
1056 ) -> Result<(), DirstateError> {
1055 let was_tracked = self.get(filename)?.map_or(false, |e| e.tracked());
1057 let was_tracked = self.get(filename)?.map_or(false, |e| e.tracked());
1056 struct Dropped {
1058 struct Dropped {
1057 was_tracked: bool,
1059 was_tracked: bool,
1058 had_entry: bool,
1060 had_entry: bool,
1059 had_copy_source: bool,
1061 had_copy_source: bool,
1060 }
1062 }
1061
1063
1062 /// If this returns `Ok(Some((dropped, removed)))`, then
1064 /// If this returns `Ok(Some((dropped, removed)))`, then
1063 ///
1065 ///
1064 /// * `dropped` is about the leaf node that was at `filename`
1066 /// * `dropped` is about the leaf node that was at `filename`
1065 /// * `removed` is whether this particular level of recursion just
1067 /// * `removed` is whether this particular level of recursion just
1066 /// removed a node in `nodes`.
1068 /// removed a node in `nodes`.
1067 fn recur<'on_disk>(
1069 fn recur<'on_disk>(
1068 on_disk: &'on_disk [u8],
1070 on_disk: &'on_disk [u8],
1069 unreachable_bytes: &mut u32,
1071 unreachable_bytes: &mut u32,
1070 nodes: &mut ChildNodes<'on_disk>,
1072 nodes: &mut ChildNodes<'on_disk>,
1071 path: &HgPath,
1073 path: &HgPath,
1072 ) -> Result<Option<(Dropped, bool)>, DirstateV2ParseError> {
1074 ) -> Result<Option<(Dropped, bool)>, DirstateV2ParseError> {
1073 let (first_path_component, rest_of_path) =
1075 let (first_path_component, rest_of_path) =
1074 path.split_first_component();
1076 path.split_first_component();
1075 let nodes = nodes.make_mut(on_disk, unreachable_bytes)?;
1077 let nodes = nodes.make_mut(on_disk, unreachable_bytes)?;
1076 let node = if let Some(node) = nodes.get_mut(first_path_component)
1078 let node = if let Some(node) = nodes.get_mut(first_path_component)
1077 {
1079 {
1078 node
1080 node
1079 } else {
1081 } else {
1080 return Ok(None);
1082 return Ok(None);
1081 };
1083 };
1082 let dropped;
1084 let dropped;
1083 if let Some(rest) = rest_of_path {
1085 if let Some(rest) = rest_of_path {
1084 if let Some((d, removed)) = recur(
1086 if let Some((d, removed)) = recur(
1085 on_disk,
1087 on_disk,
1086 unreachable_bytes,
1088 unreachable_bytes,
1087 &mut node.children,
1089 &mut node.children,
1088 rest,
1090 rest,
1089 )? {
1091 )? {
1090 dropped = d;
1092 dropped = d;
1091 if dropped.had_entry {
1093 if dropped.had_entry {
1092 node.descendants_with_entry_count = node
1094 node.descendants_with_entry_count = node
1093 .descendants_with_entry_count
1095 .descendants_with_entry_count
1094 .checked_sub(1)
1096 .checked_sub(1)
1095 .expect(
1097 .expect(
1096 "descendants_with_entry_count should be >= 0",
1098 "descendants_with_entry_count should be >= 0",
1097 );
1099 );
1098 }
1100 }
1099 if dropped.was_tracked {
1101 if dropped.was_tracked {
1100 node.tracked_descendants_count = node
1102 node.tracked_descendants_count = node
1101 .tracked_descendants_count
1103 .tracked_descendants_count
1102 .checked_sub(1)
1104 .checked_sub(1)
1103 .expect(
1105 .expect(
1104 "tracked_descendants_count should be >= 0",
1106 "tracked_descendants_count should be >= 0",
1105 );
1107 );
1106 }
1108 }
1107
1109
1108 // Directory caches must be invalidated when removing a
1110 // Directory caches must be invalidated when removing a
1109 // child node
1111 // child node
1110 if removed {
1112 if removed {
1111 if let NodeData::CachedDirectory { .. } = &node.data {
1113 if let NodeData::CachedDirectory { .. } = &node.data {
1112 node.data = NodeData::None
1114 node.data = NodeData::None
1113 }
1115 }
1114 }
1116 }
1115 } else {
1117 } else {
1116 return Ok(None);
1118 return Ok(None);
1117 }
1119 }
1118 } else {
1120 } else {
1119 let entry = node.data.as_entry();
1121 let entry = node.data.as_entry();
1120 let was_tracked = entry.map_or(false, |entry| entry.tracked());
1122 let was_tracked = entry.map_or(false, |entry| entry.tracked());
1121 let had_entry = entry.is_some();
1123 let had_entry = entry.is_some();
1122 if had_entry {
1124 if had_entry {
1123 node.data = NodeData::None
1125 node.data = NodeData::None
1124 }
1126 }
1125 let mut had_copy_source = false;
1127 let mut had_copy_source = false;
1126 if let Some(source) = &node.copy_source {
1128 if let Some(source) = &node.copy_source {
1127 DirstateMap::count_dropped_path(
1129 DirstateMap::count_dropped_path(
1128 unreachable_bytes,
1130 unreachable_bytes,
1129 Cow::Borrowed(source),
1131 Cow::Borrowed(source),
1130 );
1132 );
1131 had_copy_source = true;
1133 had_copy_source = true;
1132 node.copy_source = None
1134 node.copy_source = None
1133 }
1135 }
1134 dropped = Dropped {
1136 dropped = Dropped {
1135 was_tracked,
1137 was_tracked,
1136 had_entry,
1138 had_entry,
1137 had_copy_source,
1139 had_copy_source,
1138 };
1140 };
1139 }
1141 }
1140 // After recursion, for both leaf (rest_of_path is None) nodes and
1142 // After recursion, for both leaf (rest_of_path is None) nodes and
1141 // parent nodes, remove a node if it just became empty.
1143 // parent nodes, remove a node if it just became empty.
1142 let remove = !node.data.has_entry()
1144 let remove = !node.data.has_entry()
1143 && node.copy_source.is_none()
1145 && node.copy_source.is_none()
1144 && node.children.is_empty();
1146 && node.children.is_empty();
1145 if remove {
1147 if remove {
1146 let (key, _) =
1148 let (key, _) =
1147 nodes.remove_entry(first_path_component).unwrap();
1149 nodes.remove_entry(first_path_component).unwrap();
1148 DirstateMap::count_dropped_path(
1150 DirstateMap::count_dropped_path(
1149 unreachable_bytes,
1151 unreachable_bytes,
1150 Cow::Borrowed(key.full_path()),
1152 Cow::Borrowed(key.full_path()),
1151 )
1153 )
1152 }
1154 }
1153 Ok(Some((dropped, remove)))
1155 Ok(Some((dropped, remove)))
1154 }
1156 }
1155
1157
1156 self.with_dmap_mut(|map| {
1158 self.with_dmap_mut(|map| {
1157 if let Some((dropped, _removed)) = recur(
1159 if let Some((dropped, _removed)) = recur(
1158 map.on_disk,
1160 map.on_disk,
1159 &mut map.unreachable_bytes,
1161 &mut map.unreachable_bytes,
1160 &mut map.root,
1162 &mut map.root,
1161 filename,
1163 filename,
1162 )? {
1164 )? {
1163 if dropped.had_entry {
1165 if dropped.had_entry {
1164 map.nodes_with_entry_count = map
1166 map.nodes_with_entry_count = map
1165 .nodes_with_entry_count
1167 .nodes_with_entry_count
1166 .checked_sub(1)
1168 .checked_sub(1)
1167 .expect("nodes_with_entry_count should be >= 0");
1169 .expect("nodes_with_entry_count should be >= 0");
1168 }
1170 }
1169 if dropped.had_copy_source {
1171 if dropped.had_copy_source {
1170 map.nodes_with_copy_source_count = map
1172 map.nodes_with_copy_source_count = map
1171 .nodes_with_copy_source_count
1173 .nodes_with_copy_source_count
1172 .checked_sub(1)
1174 .checked_sub(1)
1173 .expect("nodes_with_copy_source_count should be >= 0");
1175 .expect("nodes_with_copy_source_count should be >= 0");
1174 }
1176 }
1175 } else {
1177 } else {
1176 debug_assert!(!was_tracked);
1178 debug_assert!(!was_tracked);
1177 }
1179 }
1178 Ok(())
1180 Ok(())
1179 })
1181 })
1180 }
1182 }
1181
1183
1182 pub fn has_tracked_dir(
1184 pub fn has_tracked_dir(
1183 &mut self,
1185 &mut self,
1184 directory: &HgPath,
1186 directory: &HgPath,
1185 ) -> Result<bool, DirstateError> {
1187 ) -> Result<bool, DirstateError> {
1186 self.with_dmap_mut(|map| {
1188 self.with_dmap_mut(|map| {
1187 if let Some(node) = map.get_node(directory)? {
1189 if let Some(node) = map.get_node(directory)? {
1188 // A node without a `DirstateEntry` was created to hold child
1190 // A node without a `DirstateEntry` was created to hold child
1189 // nodes, and is therefore a directory.
1191 // nodes, and is therefore a directory.
1190 let is_dir = node.entry()?.is_none();
1192 let is_dir = node.entry()?.is_none();
1191 Ok(is_dir && node.tracked_descendants_count() > 0)
1193 Ok(is_dir && node.tracked_descendants_count() > 0)
1192 } else {
1194 } else {
1193 Ok(false)
1195 Ok(false)
1194 }
1196 }
1195 })
1197 })
1196 }
1198 }
1197
1199
1198 pub fn has_dir(
1200 pub fn has_dir(
1199 &mut self,
1201 &mut self,
1200 directory: &HgPath,
1202 directory: &HgPath,
1201 ) -> Result<bool, DirstateError> {
1203 ) -> Result<bool, DirstateError> {
1202 self.with_dmap_mut(|map| {
1204 self.with_dmap_mut(|map| {
1203 if let Some(node) = map.get_node(directory)? {
1205 if let Some(node) = map.get_node(directory)? {
1204 // A node without a `DirstateEntry` was created to hold child
1206 // A node without a `DirstateEntry` was created to hold child
1205 // nodes, and is therefore a directory.
1207 // nodes, and is therefore a directory.
1206 let is_dir = node.entry()?.is_none();
1208 let is_dir = node.entry()?.is_none();
1207 Ok(is_dir && node.descendants_with_entry_count() > 0)
1209 Ok(is_dir && node.descendants_with_entry_count() > 0)
1208 } else {
1210 } else {
1209 Ok(false)
1211 Ok(false)
1210 }
1212 }
1211 })
1213 })
1212 }
1214 }
1213
1215
1214 #[logging_timer::time("trace")]
1216 #[logging_timer::time("trace")]
1215 pub fn pack_v1(
1217 pub fn pack_v1(
1216 &self,
1218 &self,
1217 parents: DirstateParents,
1219 parents: DirstateParents,
1218 ) -> Result<Vec<u8>, DirstateError> {
1220 ) -> Result<Vec<u8>, DirstateError> {
1219 let map = self.get_map();
1221 let map = self.get_map();
1220 // Optizimation (to be measured?): pre-compute size to avoid `Vec`
1222 // Optizimation (to be measured?): pre-compute size to avoid `Vec`
1221 // reallocations
1223 // reallocations
1222 let mut size = parents.as_bytes().len();
1224 let mut size = parents.as_bytes().len();
1223 for node in map.iter_nodes() {
1225 for node in map.iter_nodes() {
1224 let node = node?;
1226 let node = node?;
1225 if node.entry()?.is_some() {
1227 if node.entry()?.is_some() {
1226 size += packed_entry_size(
1228 size += packed_entry_size(
1227 node.full_path(map.on_disk)?,
1229 node.full_path(map.on_disk)?,
1228 node.copy_source(map.on_disk)?,
1230 node.copy_source(map.on_disk)?,
1229 );
1231 );
1230 }
1232 }
1231 }
1233 }
1232
1234
1233 let mut packed = Vec::with_capacity(size);
1235 let mut packed = Vec::with_capacity(size);
1234 packed.extend(parents.as_bytes());
1236 packed.extend(parents.as_bytes());
1235
1237
1236 for node in map.iter_nodes() {
1238 for node in map.iter_nodes() {
1237 let node = node?;
1239 let node = node?;
1238 if let Some(entry) = node.entry()? {
1240 if let Some(entry) = node.entry()? {
1239 pack_entry(
1241 pack_entry(
1240 node.full_path(map.on_disk)?,
1242 node.full_path(map.on_disk)?,
1241 &entry,
1243 &entry,
1242 node.copy_source(map.on_disk)?,
1244 node.copy_source(map.on_disk)?,
1243 &mut packed,
1245 &mut packed,
1244 );
1246 );
1245 }
1247 }
1246 }
1248 }
1247 Ok(packed)
1249 Ok(packed)
1248 }
1250 }
1249
1251
1250 /// Returns new data and metadata together with whether that data should be
1252 /// Returns new data and metadata together with whether that data should be
1251 /// appended to the existing data file whose content is at
1253 /// appended to the existing data file whose content is at
1252 /// `map.on_disk` (true), instead of written to a new data file
1254 /// `map.on_disk` (true), instead of written to a new data file
1253 /// (false), and the previous size of data on disk.
1255 /// (false), and the previous size of data on disk.
1254 #[logging_timer::time("trace")]
1256 #[logging_timer::time("trace")]
1255 pub fn pack_v2(
1257 pub fn pack_v2(
1256 &self,
1258 &self,
1257 can_append: bool,
1259 can_append: bool,
1258 ) -> Result<(Vec<u8>, on_disk::TreeMetadata, bool, usize), DirstateError>
1260 ) -> Result<(Vec<u8>, on_disk::TreeMetadata, bool, usize), DirstateError>
1259 {
1261 {
1260 let map = self.get_map();
1262 let map = self.get_map();
1261 on_disk::write(map, can_append)
1263 on_disk::write(map, can_append)
1262 }
1264 }
1263
1265
1264 /// `callback` allows the caller to process and do something with the
1266 /// `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.
1267 /// results of the status. This is needed to do so efficiently (i.e.
1266 /// without cloning the `DirstateStatus` object with its paths) because
1268 /// without cloning the `DirstateStatus` object with its paths) because
1267 /// we need to borrow from `Self`.
1269 /// we need to borrow from `Self`.
1268 pub fn with_status<R>(
1270 pub fn with_status<R>(
1269 &mut self,
1271 &mut self,
1270 matcher: &(dyn Matcher + Sync),
1272 matcher: &(dyn Matcher + Sync),
1271 root_dir: PathBuf,
1273 root_dir: PathBuf,
1272 ignore_files: Vec<PathBuf>,
1274 ignore_files: Vec<PathBuf>,
1273 options: StatusOptions,
1275 options: StatusOptions,
1274 callback: impl for<'r> FnOnce(
1276 callback: impl for<'r> FnOnce(
1275 Result<(DirstateStatus<'r>, Vec<PatternFileWarning>), StatusError>,
1277 Result<(DirstateStatus<'r>, Vec<PatternFileWarning>), StatusError>,
1276 ) -> R,
1278 ) -> R,
1277 ) -> R {
1279 ) -> R {
1278 self.with_dmap_mut(|map| {
1280 self.with_dmap_mut(|map| {
1279 callback(super::status::status(
1281 callback(super::status::status(
1280 map,
1282 map,
1281 matcher,
1283 matcher,
1282 root_dir,
1284 root_dir,
1283 ignore_files,
1285 ignore_files,
1284 options,
1286 options,
1285 ))
1287 ))
1286 })
1288 })
1287 }
1289 }
1288
1290
1289 pub fn copy_map_len(&self) -> usize {
1291 pub fn copy_map_len(&self) -> usize {
1290 let map = self.get_map();
1292 let map = self.get_map();
1291 map.nodes_with_copy_source_count as usize
1293 map.nodes_with_copy_source_count as usize
1292 }
1294 }
1293
1295
1294 pub fn copy_map_iter(&self) -> CopyMapIter<'_> {
1296 pub fn copy_map_iter(&self) -> CopyMapIter<'_> {
1295 let map = self.get_map();
1297 let map = self.get_map();
1296 Box::new(filter_map_results(map.iter_nodes(), move |node| {
1298 Box::new(filter_map_results(map.iter_nodes(), move |node| {
1297 Ok(if let Some(source) = node.copy_source(map.on_disk)? {
1299 Ok(if let Some(source) = node.copy_source(map.on_disk)? {
1298 Some((node.full_path(map.on_disk)?, source))
1300 Some((node.full_path(map.on_disk)?, source))
1299 } else {
1301 } else {
1300 None
1302 None
1301 })
1303 })
1302 }))
1304 }))
1303 }
1305 }
1304
1306
1305 pub fn copy_map_contains_key(
1307 pub fn copy_map_contains_key(
1306 &self,
1308 &self,
1307 key: &HgPath,
1309 key: &HgPath,
1308 ) -> Result<bool, DirstateV2ParseError> {
1310 ) -> Result<bool, DirstateV2ParseError> {
1309 let map = self.get_map();
1311 let map = self.get_map();
1310 Ok(if let Some(node) = map.get_node(key)? {
1312 Ok(if let Some(node) = map.get_node(key)? {
1311 node.has_copy_source()
1313 node.has_copy_source()
1312 } else {
1314 } else {
1313 false
1315 false
1314 })
1316 })
1315 }
1317 }
1316
1318
1317 pub fn copy_map_get(
1319 pub fn copy_map_get(
1318 &self,
1320 &self,
1319 key: &HgPath,
1321 key: &HgPath,
1320 ) -> Result<Option<&HgPath>, DirstateV2ParseError> {
1322 ) -> Result<Option<&HgPath>, DirstateV2ParseError> {
1321 let map = self.get_map();
1323 let map = self.get_map();
1322 if let Some(node) = map.get_node(key)? {
1324 if let Some(node) = map.get_node(key)? {
1323 if let Some(source) = node.copy_source(map.on_disk)? {
1325 if let Some(source) = node.copy_source(map.on_disk)? {
1324 return Ok(Some(source));
1326 return Ok(Some(source));
1325 }
1327 }
1326 }
1328 }
1327 Ok(None)
1329 Ok(None)
1328 }
1330 }
1329
1331
1330 pub fn copy_map_remove(
1332 pub fn copy_map_remove(
1331 &mut self,
1333 &mut self,
1332 key: &HgPath,
1334 key: &HgPath,
1333 ) -> Result<Option<HgPathBuf>, DirstateV2ParseError> {
1335 ) -> Result<Option<HgPathBuf>, DirstateV2ParseError> {
1334 self.with_dmap_mut(|map| {
1336 self.with_dmap_mut(|map| {
1335 let count = &mut map.nodes_with_copy_source_count;
1337 let count = &mut map.nodes_with_copy_source_count;
1336 let unreachable_bytes = &mut map.unreachable_bytes;
1338 let unreachable_bytes = &mut map.unreachable_bytes;
1337 Ok(DirstateMap::get_node_mut_inner(
1339 Ok(DirstateMap::get_node_mut_inner(
1338 map.on_disk,
1340 map.on_disk,
1339 unreachable_bytes,
1341 unreachable_bytes,
1340 &mut map.root,
1342 &mut map.root,
1341 key,
1343 key,
1342 |_ancestor| {},
1344 |_ancestor| {},
1343 )?
1345 )?
1344 .and_then(|node| {
1346 .and_then(|node| {
1345 if let Some(source) = &node.copy_source {
1347 if let Some(source) = &node.copy_source {
1346 *count = count
1348 *count = count
1347 .checked_sub(1)
1349 .checked_sub(1)
1348 .expect("nodes_with_copy_source_count should be >= 0");
1350 .expect("nodes_with_copy_source_count should be >= 0");
1349 DirstateMap::count_dropped_path(
1351 DirstateMap::count_dropped_path(
1350 unreachable_bytes,
1352 unreachable_bytes,
1351 Cow::Borrowed(source),
1353 Cow::Borrowed(source),
1352 );
1354 );
1353 }
1355 }
1354 node.copy_source.take().map(Cow::into_owned)
1356 node.copy_source.take().map(Cow::into_owned)
1355 }))
1357 }))
1356 })
1358 })
1357 }
1359 }
1358
1360
1359 pub fn copy_map_insert(
1361 pub fn copy_map_insert(
1360 &mut self,
1362 &mut self,
1361 key: &HgPath,
1363 key: &HgPath,
1362 value: &HgPath,
1364 value: &HgPath,
1363 ) -> Result<Option<HgPathBuf>, DirstateV2ParseError> {
1365 ) -> Result<Option<HgPathBuf>, DirstateV2ParseError> {
1364 self.with_dmap_mut(|map| {
1366 self.with_dmap_mut(|map| {
1365 let node = map.get_or_insert_node(&key, |_ancestor| {})?;
1367 let node = map.get_or_insert_node(&key, |_ancestor| {})?;
1366 let had_copy_source = node.copy_source.is_none();
1368 let had_copy_source = node.copy_source.is_none();
1367 let old = node
1369 let old = node
1368 .copy_source
1370 .copy_source
1369 .replace(value.to_owned().into())
1371 .replace(value.to_owned().into())
1370 .map(Cow::into_owned);
1372 .map(Cow::into_owned);
1371 if had_copy_source {
1373 if had_copy_source {
1372 map.nodes_with_copy_source_count += 1
1374 map.nodes_with_copy_source_count += 1
1373 }
1375 }
1374 Ok(old)
1376 Ok(old)
1375 })
1377 })
1376 }
1378 }
1377
1379
1378 pub fn len(&self) -> usize {
1380 pub fn len(&self) -> usize {
1379 let map = self.get_map();
1381 let map = self.get_map();
1380 map.nodes_with_entry_count as usize
1382 map.nodes_with_entry_count as usize
1381 }
1383 }
1382
1384
1383 pub fn is_empty(&self) -> bool {
1385 pub fn is_empty(&self) -> bool {
1384 self.len() == 0
1386 self.len() == 0
1385 }
1387 }
1386
1388
1387 pub fn contains_key(
1389 pub fn contains_key(
1388 &self,
1390 &self,
1389 key: &HgPath,
1391 key: &HgPath,
1390 ) -> Result<bool, DirstateV2ParseError> {
1392 ) -> Result<bool, DirstateV2ParseError> {
1391 Ok(self.get(key)?.is_some())
1393 Ok(self.get(key)?.is_some())
1392 }
1394 }
1393
1395
1394 pub fn get(
1396 pub fn get(
1395 &self,
1397 &self,
1396 key: &HgPath,
1398 key: &HgPath,
1397 ) -> Result<Option<DirstateEntry>, DirstateV2ParseError> {
1399 ) -> Result<Option<DirstateEntry>, DirstateV2ParseError> {
1398 let map = self.get_map();
1400 let map = self.get_map();
1399 Ok(if let Some(node) = map.get_node(key)? {
1401 Ok(if let Some(node) = map.get_node(key)? {
1400 node.entry()?
1402 node.entry()?
1401 } else {
1403 } else {
1402 None
1404 None
1403 })
1405 })
1404 }
1406 }
1405
1407
1406 pub fn iter(&self) -> StateMapIter<'_> {
1408 pub fn iter(&self) -> StateMapIter<'_> {
1407 let map = self.get_map();
1409 let map = self.get_map();
1408 Box::new(filter_map_results(map.iter_nodes(), move |node| {
1410 Box::new(filter_map_results(map.iter_nodes(), move |node| {
1409 Ok(if let Some(entry) = node.entry()? {
1411 Ok(if let Some(entry) = node.entry()? {
1410 Some((node.full_path(map.on_disk)?, entry))
1412 Some((node.full_path(map.on_disk)?, entry))
1411 } else {
1413 } else {
1412 None
1414 None
1413 })
1415 })
1414 }))
1416 }))
1415 }
1417 }
1416
1418
1417 pub fn iter_tracked_dirs(
1419 pub fn iter_tracked_dirs(
1418 &mut self,
1420 &mut self,
1419 ) -> Result<
1421 ) -> Result<
1420 Box<
1422 Box<
1421 dyn Iterator<Item = Result<&HgPath, DirstateV2ParseError>>
1423 dyn Iterator<Item = Result<&HgPath, DirstateV2ParseError>>
1422 + Send
1424 + Send
1423 + '_,
1425 + '_,
1424 >,
1426 >,
1425 DirstateError,
1427 DirstateError,
1426 > {
1428 > {
1427 let map = self.get_map();
1429 let map = self.get_map();
1428 let on_disk = map.on_disk;
1430 let on_disk = map.on_disk;
1429 Ok(Box::new(filter_map_results(
1431 Ok(Box::new(filter_map_results(
1430 map.iter_nodes(),
1432 map.iter_nodes(),
1431 move |node| {
1433 move |node| {
1432 Ok(if node.tracked_descendants_count() > 0 {
1434 Ok(if node.tracked_descendants_count() > 0 {
1433 Some(node.full_path(on_disk)?)
1435 Some(node.full_path(on_disk)?)
1434 } else {
1436 } else {
1435 None
1437 None
1436 })
1438 })
1437 },
1439 },
1438 )))
1440 )))
1439 }
1441 }
1440
1442
1441 /// Only public because it needs to be exposed to the Python layer.
1443 /// Only public because it needs to be exposed to the Python layer.
1442 /// It is not the full `setparents` logic, only the parts that mutate the
1444 /// It is not the full `setparents` logic, only the parts that mutate the
1443 /// entries.
1445 /// entries.
1444 pub fn setparents_fixup(
1446 pub fn setparents_fixup(
1445 &mut self,
1447 &mut self,
1446 ) -> Result<Vec<(HgPathBuf, HgPathBuf)>, DirstateV2ParseError> {
1448 ) -> Result<Vec<(HgPathBuf, HgPathBuf)>, DirstateV2ParseError> {
1447 // XXX
1449 // XXX
1448 // All the copying and re-querying is quite inefficient, but this is
1450 // All the copying and re-querying is quite inefficient, but this is
1449 // still a lot better than doing it from Python.
1451 // still a lot better than doing it from Python.
1450 //
1452 //
1451 // The better solution is to develop a mechanism for `iter_mut`,
1453 // The better solution is to develop a mechanism for `iter_mut`,
1452 // which will be a lot more involved: we're dealing with a lazy,
1454 // which will be a lot more involved: we're dealing with a lazy,
1453 // append-mostly, tree-like data structure. This will do for now.
1455 // append-mostly, tree-like data structure. This will do for now.
1454 let mut copies = vec![];
1456 let mut copies = vec![];
1455 let mut files_with_p2_info = vec![];
1457 let mut files_with_p2_info = vec![];
1456 for res in self.iter() {
1458 for res in self.iter() {
1457 let (path, entry) = res?;
1459 let (path, entry) = res?;
1458 if entry.p2_info() {
1460 if entry.p2_info() {
1459 files_with_p2_info.push(path.to_owned())
1461 files_with_p2_info.push(path.to_owned())
1460 }
1462 }
1461 }
1463 }
1462 self.with_dmap_mut(|map| {
1464 self.with_dmap_mut(|map| {
1463 for path in files_with_p2_info.iter() {
1465 for path in files_with_p2_info.iter() {
1464 let node = map.get_or_insert_node(path, |_| {})?;
1466 let node = map.get_or_insert_node(path, |_| {})?;
1465 let entry =
1467 let entry =
1466 node.data.as_entry_mut().expect("entry should exist");
1468 node.data.as_entry_mut().expect("entry should exist");
1467 entry.drop_merge_data();
1469 entry.drop_merge_data();
1468 if let Some(source) = node.copy_source.take().as_deref() {
1470 if let Some(source) = node.copy_source.take().as_deref() {
1469 copies.push((path.to_owned(), source.to_owned()));
1471 copies.push((path.to_owned(), source.to_owned()));
1470 }
1472 }
1471 }
1473 }
1472 Ok(copies)
1474 Ok(copies)
1473 })
1475 })
1474 }
1476 }
1475
1477
1476 pub fn debug_iter(
1478 pub fn debug_iter(
1477 &self,
1479 &self,
1478 all: bool,
1480 all: bool,
1479 ) -> Box<
1481 ) -> Box<
1480 dyn Iterator<
1482 dyn Iterator<Item = Result<DebugDirstateTuple, DirstateV2ParseError>>
1481 Item = Result<
1483 + Send
1482 (&HgPath, (u8, i32, i32, i32)),
1483 DirstateV2ParseError,
1484 >,
1485 > + Send
1486 + '_,
1484 + '_,
1487 > {
1485 > {
1488 let map = self.get_map();
1486 let map = self.get_map();
1489 Box::new(filter_map_results(map.iter_nodes(), move |node| {
1487 Box::new(filter_map_results(map.iter_nodes(), move |node| {
1490 let debug_tuple = if let Some(entry) = node.entry()? {
1488 let debug_tuple = if let Some(entry) = node.entry()? {
1491 entry.debug_tuple()
1489 entry.debug_tuple()
1492 } else if !all {
1490 } else if !all {
1493 return Ok(None);
1491 return Ok(None);
1494 } else if let Some(mtime) = node.cached_directory_mtime()? {
1492 } else if let Some(mtime) = node.cached_directory_mtime()? {
1495 (b' ', 0, -1, mtime.truncated_seconds() as i32)
1493 (b' ', 0, -1, mtime.truncated_seconds() as i32)
1496 } else {
1494 } else {
1497 (b' ', 0, -1, -1)
1495 (b' ', 0, -1, -1)
1498 };
1496 };
1499 Ok(Some((node.full_path(map.on_disk)?, debug_tuple)))
1497 Ok(Some((node.full_path(map.on_disk)?, debug_tuple)))
1500 }))
1498 }))
1501 }
1499 }
1502 }
1500 }
1503 #[cfg(test)]
1501 #[cfg(test)]
1504 mod tests {
1502 mod tests {
1505 use super::*;
1503 use super::*;
1506
1504
1507 /// Shortcut to return tracked descendants of a path.
1505 /// Shortcut to return tracked descendants of a path.
1508 /// Panics if the path does not exist.
1506 /// Panics if the path does not exist.
1509 fn tracked_descendants(map: &OwningDirstateMap, path: &[u8]) -> u32 {
1507 fn tracked_descendants(map: &OwningDirstateMap, path: &[u8]) -> u32 {
1510 let path = dbg!(HgPath::new(path));
1508 let path = dbg!(HgPath::new(path));
1511 let node = map.get_map().get_node(path);
1509 let node = map.get_map().get_node(path);
1512 node.unwrap().unwrap().tracked_descendants_count()
1510 node.unwrap().unwrap().tracked_descendants_count()
1513 }
1511 }
1514
1512
1515 /// Shortcut to return descendants with an entry.
1513 /// Shortcut to return descendants with an entry.
1516 /// Panics if the path does not exist.
1514 /// Panics if the path does not exist.
1517 fn descendants_with_an_entry(map: &OwningDirstateMap, path: &[u8]) -> u32 {
1515 fn descendants_with_an_entry(map: &OwningDirstateMap, path: &[u8]) -> u32 {
1518 let path = dbg!(HgPath::new(path));
1516 let path = dbg!(HgPath::new(path));
1519 let node = map.get_map().get_node(path);
1517 let node = map.get_map().get_node(path);
1520 node.unwrap().unwrap().descendants_with_entry_count()
1518 node.unwrap().unwrap().descendants_with_entry_count()
1521 }
1519 }
1522
1520
1523 fn assert_does_not_exist(map: &OwningDirstateMap, path: &[u8]) {
1521 fn assert_does_not_exist(map: &OwningDirstateMap, path: &[u8]) {
1524 let path = dbg!(HgPath::new(path));
1522 let path = dbg!(HgPath::new(path));
1525 let node = map.get_map().get_node(path);
1523 let node = map.get_map().get_node(path);
1526 assert!(node.unwrap().is_none());
1524 assert!(node.unwrap().is_none());
1527 }
1525 }
1528
1526
1529 /// Shortcut for path creation in tests
1527 /// Shortcut for path creation in tests
1530 fn p(b: &[u8]) -> &HgPath {
1528 fn p(b: &[u8]) -> &HgPath {
1531 HgPath::new(b)
1529 HgPath::new(b)
1532 }
1530 }
1533
1531
1534 /// Test the very simple case a single tracked file
1532 /// Test the very simple case a single tracked file
1535 #[test]
1533 #[test]
1536 fn test_tracked_descendants_simple() -> Result<(), DirstateError> {
1534 fn test_tracked_descendants_simple() -> Result<(), DirstateError> {
1537 let mut map = OwningDirstateMap::new_empty(vec![]);
1535 let mut map = OwningDirstateMap::new_empty(vec![]);
1538 assert_eq!(map.len(), 0);
1536 assert_eq!(map.len(), 0);
1539
1537
1540 map.set_tracked(p(b"some/nested/path"))?;
1538 map.set_tracked(p(b"some/nested/path"))?;
1541
1539
1542 assert_eq!(map.len(), 1);
1540 assert_eq!(map.len(), 1);
1543 assert_eq!(tracked_descendants(&map, b"some"), 1);
1541 assert_eq!(tracked_descendants(&map, b"some"), 1);
1544 assert_eq!(tracked_descendants(&map, b"some/nested"), 1);
1542 assert_eq!(tracked_descendants(&map, b"some/nested"), 1);
1545 assert_eq!(tracked_descendants(&map, b"some/nested/path"), 0);
1543 assert_eq!(tracked_descendants(&map, b"some/nested/path"), 0);
1546
1544
1547 map.set_untracked(p(b"some/nested/path"))?;
1545 map.set_untracked(p(b"some/nested/path"))?;
1548 assert_eq!(map.len(), 0);
1546 assert_eq!(map.len(), 0);
1549 assert!(map.get_map().get_node(p(b"some"))?.is_none());
1547 assert!(map.get_map().get_node(p(b"some"))?.is_none());
1550
1548
1551 Ok(())
1549 Ok(())
1552 }
1550 }
1553
1551
1554 /// Test the simple case of all tracked, but multiple files
1552 /// Test the simple case of all tracked, but multiple files
1555 #[test]
1553 #[test]
1556 fn test_tracked_descendants_multiple() -> Result<(), DirstateError> {
1554 fn test_tracked_descendants_multiple() -> Result<(), DirstateError> {
1557 let mut map = OwningDirstateMap::new_empty(vec![]);
1555 let mut map = OwningDirstateMap::new_empty(vec![]);
1558
1556
1559 map.set_tracked(p(b"some/nested/path"))?;
1557 map.set_tracked(p(b"some/nested/path"))?;
1560 map.set_tracked(p(b"some/nested/file"))?;
1558 map.set_tracked(p(b"some/nested/file"))?;
1561 // one layer without any files to test deletion cascade
1559 // one layer without any files to test deletion cascade
1562 map.set_tracked(p(b"some/other/nested/path"))?;
1560 map.set_tracked(p(b"some/other/nested/path"))?;
1563 map.set_tracked(p(b"root_file"))?;
1561 map.set_tracked(p(b"root_file"))?;
1564 map.set_tracked(p(b"some/file"))?;
1562 map.set_tracked(p(b"some/file"))?;
1565 map.set_tracked(p(b"some/file2"))?;
1563 map.set_tracked(p(b"some/file2"))?;
1566 map.set_tracked(p(b"some/file3"))?;
1564 map.set_tracked(p(b"some/file3"))?;
1567
1565
1568 assert_eq!(map.len(), 7);
1566 assert_eq!(map.len(), 7);
1569 assert_eq!(tracked_descendants(&map, b"some"), 6);
1567 assert_eq!(tracked_descendants(&map, b"some"), 6);
1570 assert_eq!(tracked_descendants(&map, b"some/nested"), 2);
1568 assert_eq!(tracked_descendants(&map, b"some/nested"), 2);
1571 assert_eq!(tracked_descendants(&map, b"some/other"), 1);
1569 assert_eq!(tracked_descendants(&map, b"some/other"), 1);
1572 assert_eq!(tracked_descendants(&map, b"some/other/nested"), 1);
1570 assert_eq!(tracked_descendants(&map, b"some/other/nested"), 1);
1573 assert_eq!(tracked_descendants(&map, b"some/nested/path"), 0);
1571 assert_eq!(tracked_descendants(&map, b"some/nested/path"), 0);
1574
1572
1575 map.set_untracked(p(b"some/nested/path"))?;
1573 map.set_untracked(p(b"some/nested/path"))?;
1576 assert_eq!(map.len(), 6);
1574 assert_eq!(map.len(), 6);
1577 assert_eq!(tracked_descendants(&map, b"some"), 5);
1575 assert_eq!(tracked_descendants(&map, b"some"), 5);
1578 assert_eq!(tracked_descendants(&map, b"some/nested"), 1);
1576 assert_eq!(tracked_descendants(&map, b"some/nested"), 1);
1579 assert_eq!(tracked_descendants(&map, b"some/other"), 1);
1577 assert_eq!(tracked_descendants(&map, b"some/other"), 1);
1580 assert_eq!(tracked_descendants(&map, b"some/other/nested"), 1);
1578 assert_eq!(tracked_descendants(&map, b"some/other/nested"), 1);
1581
1579
1582 map.set_untracked(p(b"some/nested/file"))?;
1580 map.set_untracked(p(b"some/nested/file"))?;
1583 assert_eq!(map.len(), 5);
1581 assert_eq!(map.len(), 5);
1584 assert_eq!(tracked_descendants(&map, b"some"), 4);
1582 assert_eq!(tracked_descendants(&map, b"some"), 4);
1585 assert_eq!(tracked_descendants(&map, b"some/other"), 1);
1583 assert_eq!(tracked_descendants(&map, b"some/other"), 1);
1586 assert_eq!(tracked_descendants(&map, b"some/other/nested"), 1);
1584 assert_eq!(tracked_descendants(&map, b"some/other/nested"), 1);
1587 assert_does_not_exist(&map, b"some_nested");
1585 assert_does_not_exist(&map, b"some_nested");
1588
1586
1589 map.set_untracked(p(b"some/other/nested/path"))?;
1587 map.set_untracked(p(b"some/other/nested/path"))?;
1590 assert_eq!(map.len(), 4);
1588 assert_eq!(map.len(), 4);
1591 assert_eq!(tracked_descendants(&map, b"some"), 3);
1589 assert_eq!(tracked_descendants(&map, b"some"), 3);
1592 assert_does_not_exist(&map, b"some/other");
1590 assert_does_not_exist(&map, b"some/other");
1593
1591
1594 map.set_untracked(p(b"root_file"))?;
1592 map.set_untracked(p(b"root_file"))?;
1595 assert_eq!(map.len(), 3);
1593 assert_eq!(map.len(), 3);
1596 assert_eq!(tracked_descendants(&map, b"some"), 3);
1594 assert_eq!(tracked_descendants(&map, b"some"), 3);
1597 assert_does_not_exist(&map, b"root_file");
1595 assert_does_not_exist(&map, b"root_file");
1598
1596
1599 map.set_untracked(p(b"some/file"))?;
1597 map.set_untracked(p(b"some/file"))?;
1600 assert_eq!(map.len(), 2);
1598 assert_eq!(map.len(), 2);
1601 assert_eq!(tracked_descendants(&map, b"some"), 2);
1599 assert_eq!(tracked_descendants(&map, b"some"), 2);
1602 assert_does_not_exist(&map, b"some/file");
1600 assert_does_not_exist(&map, b"some/file");
1603
1601
1604 map.set_untracked(p(b"some/file2"))?;
1602 map.set_untracked(p(b"some/file2"))?;
1605 assert_eq!(map.len(), 1);
1603 assert_eq!(map.len(), 1);
1606 assert_eq!(tracked_descendants(&map, b"some"), 1);
1604 assert_eq!(tracked_descendants(&map, b"some"), 1);
1607 assert_does_not_exist(&map, b"some/file2");
1605 assert_does_not_exist(&map, b"some/file2");
1608
1606
1609 map.set_untracked(p(b"some/file3"))?;
1607 map.set_untracked(p(b"some/file3"))?;
1610 assert_eq!(map.len(), 0);
1608 assert_eq!(map.len(), 0);
1611 assert_does_not_exist(&map, b"some/file3");
1609 assert_does_not_exist(&map, b"some/file3");
1612
1610
1613 Ok(())
1611 Ok(())
1614 }
1612 }
1615
1613
1616 /// Check with a mix of tracked and non-tracked items
1614 /// Check with a mix of tracked and non-tracked items
1617 #[test]
1615 #[test]
1618 fn test_tracked_descendants_different() -> Result<(), DirstateError> {
1616 fn test_tracked_descendants_different() -> Result<(), DirstateError> {
1619 let mut map = OwningDirstateMap::new_empty(vec![]);
1617 let mut map = OwningDirstateMap::new_empty(vec![]);
1620
1618
1621 // A file that was just added
1619 // A file that was just added
1622 map.set_tracked(p(b"some/nested/path"))?;
1620 map.set_tracked(p(b"some/nested/path"))?;
1623 // This has no information, the dirstate should ignore it
1621 // This has no information, the dirstate should ignore it
1624 map.reset_state(p(b"some/file"), false, false, false, false, None)?;
1622 map.reset_state(p(b"some/file"), false, false, false, false, None)?;
1625 assert_does_not_exist(&map, b"some/file");
1623 assert_does_not_exist(&map, b"some/file");
1626
1624
1627 // A file that was removed
1625 // A file that was removed
1628 map.reset_state(
1626 map.reset_state(
1629 p(b"some/nested/file"),
1627 p(b"some/nested/file"),
1630 false,
1628 false,
1631 true,
1629 true,
1632 false,
1630 false,
1633 false,
1631 false,
1634 None,
1632 None,
1635 )?;
1633 )?;
1636 assert!(!map.get(p(b"some/nested/file"))?.unwrap().tracked());
1634 assert!(!map.get(p(b"some/nested/file"))?.unwrap().tracked());
1637 // Only present in p2
1635 // Only present in p2
1638 map.reset_state(p(b"some/file3"), false, false, true, false, None)?;
1636 map.reset_state(p(b"some/file3"), false, false, true, false, None)?;
1639 assert!(!map.get(p(b"some/file3"))?.unwrap().tracked());
1637 assert!(!map.get(p(b"some/file3"))?.unwrap().tracked());
1640 // A file that was merged
1638 // A file that was merged
1641 map.reset_state(p(b"root_file"), true, true, true, false, None)?;
1639 map.reset_state(p(b"root_file"), true, true, true, false, None)?;
1642 assert!(map.get(p(b"root_file"))?.unwrap().tracked());
1640 assert!(map.get(p(b"root_file"))?.unwrap().tracked());
1643 // A file that is added, with info from p2
1641 // A file that is added, with info from p2
1644 // XXX is that actually possible?
1642 // XXX is that actually possible?
1645 map.reset_state(p(b"some/file2"), true, false, true, false, None)?;
1643 map.reset_state(p(b"some/file2"), true, false, true, false, None)?;
1646 assert!(map.get(p(b"some/file2"))?.unwrap().tracked());
1644 assert!(map.get(p(b"some/file2"))?.unwrap().tracked());
1647 // A clean file
1645 // A clean file
1648 // One layer without any files to test deletion cascade
1646 // One layer without any files to test deletion cascade
1649 map.reset_state(
1647 map.reset_state(
1650 p(b"some/other/nested/path"),
1648 p(b"some/other/nested/path"),
1651 true,
1649 true,
1652 true,
1650 true,
1653 false,
1651 false,
1654 false,
1652 false,
1655 None,
1653 None,
1656 )?;
1654 )?;
1657 assert!(map.get(p(b"some/other/nested/path"))?.unwrap().tracked());
1655 assert!(map.get(p(b"some/other/nested/path"))?.unwrap().tracked());
1658
1656
1659 assert_eq!(map.len(), 6);
1657 assert_eq!(map.len(), 6);
1660 assert_eq!(tracked_descendants(&map, b"some"), 3);
1658 assert_eq!(tracked_descendants(&map, b"some"), 3);
1661 assert_eq!(descendants_with_an_entry(&map, b"some"), 5);
1659 assert_eq!(descendants_with_an_entry(&map, b"some"), 5);
1662 assert_eq!(tracked_descendants(&map, b"some/other/nested"), 1);
1660 assert_eq!(tracked_descendants(&map, b"some/other/nested"), 1);
1663 assert_eq!(descendants_with_an_entry(&map, b"some/other/nested"), 1);
1661 assert_eq!(descendants_with_an_entry(&map, b"some/other/nested"), 1);
1664 assert_eq!(tracked_descendants(&map, b"some/other/nested/path"), 0);
1662 assert_eq!(tracked_descendants(&map, b"some/other/nested/path"), 0);
1665 assert_eq!(
1663 assert_eq!(
1666 descendants_with_an_entry(&map, b"some/other/nested/path"),
1664 descendants_with_an_entry(&map, b"some/other/nested/path"),
1667 0
1665 0
1668 );
1666 );
1669 assert_eq!(tracked_descendants(&map, b"some/nested"), 1);
1667 assert_eq!(tracked_descendants(&map, b"some/nested"), 1);
1670 assert_eq!(descendants_with_an_entry(&map, b"some/nested"), 2);
1668 assert_eq!(descendants_with_an_entry(&map, b"some/nested"), 2);
1671
1669
1672 // might as well check this
1670 // might as well check this
1673 map.set_untracked(p(b"path/does/not/exist"))?;
1671 map.set_untracked(p(b"path/does/not/exist"))?;
1674 assert_eq!(map.len(), 6);
1672 assert_eq!(map.len(), 6);
1675
1673
1676 map.set_untracked(p(b"some/other/nested/path"))?;
1674 map.set_untracked(p(b"some/other/nested/path"))?;
1677 // It is set untracked but not deleted since it held other information
1675 // It is set untracked but not deleted since it held other information
1678 assert_eq!(map.len(), 6);
1676 assert_eq!(map.len(), 6);
1679 assert_eq!(tracked_descendants(&map, b"some"), 2);
1677 assert_eq!(tracked_descendants(&map, b"some"), 2);
1680 assert_eq!(descendants_with_an_entry(&map, b"some"), 5);
1678 assert_eq!(descendants_with_an_entry(&map, b"some"), 5);
1681 assert_eq!(descendants_with_an_entry(&map, b"some/other"), 1);
1679 assert_eq!(descendants_with_an_entry(&map, b"some/other"), 1);
1682 assert_eq!(descendants_with_an_entry(&map, b"some/other/nested"), 1);
1680 assert_eq!(descendants_with_an_entry(&map, b"some/other/nested"), 1);
1683 assert_eq!(tracked_descendants(&map, b"some/nested"), 1);
1681 assert_eq!(tracked_descendants(&map, b"some/nested"), 1);
1684 assert_eq!(descendants_with_an_entry(&map, b"some/nested"), 2);
1682 assert_eq!(descendants_with_an_entry(&map, b"some/nested"), 2);
1685
1683
1686 map.set_untracked(p(b"some/nested/path"))?;
1684 map.set_untracked(p(b"some/nested/path"))?;
1687 // It is set untracked *and* deleted since it was only added
1685 // It is set untracked *and* deleted since it was only added
1688 assert_eq!(map.len(), 5);
1686 assert_eq!(map.len(), 5);
1689 assert_eq!(tracked_descendants(&map, b"some"), 1);
1687 assert_eq!(tracked_descendants(&map, b"some"), 1);
1690 assert_eq!(descendants_with_an_entry(&map, b"some"), 4);
1688 assert_eq!(descendants_with_an_entry(&map, b"some"), 4);
1691 assert_eq!(tracked_descendants(&map, b"some/nested"), 0);
1689 assert_eq!(tracked_descendants(&map, b"some/nested"), 0);
1692 assert_eq!(descendants_with_an_entry(&map, b"some/nested"), 1);
1690 assert_eq!(descendants_with_an_entry(&map, b"some/nested"), 1);
1693 assert_does_not_exist(&map, b"some/nested/path");
1691 assert_does_not_exist(&map, b"some/nested/path");
1694
1692
1695 map.set_untracked(p(b"root_file"))?;
1693 map.set_untracked(p(b"root_file"))?;
1696 // Untracked but not deleted
1694 // Untracked but not deleted
1697 assert_eq!(map.len(), 5);
1695 assert_eq!(map.len(), 5);
1698 assert!(map.get(p(b"root_file"))?.is_some());
1696 assert!(map.get(p(b"root_file"))?.is_some());
1699
1697
1700 map.set_untracked(p(b"some/file2"))?;
1698 map.set_untracked(p(b"some/file2"))?;
1701 assert_eq!(map.len(), 5);
1699 assert_eq!(map.len(), 5);
1702 assert_eq!(tracked_descendants(&map, b"some"), 0);
1700 assert_eq!(tracked_descendants(&map, b"some"), 0);
1703 assert!(map.get(p(b"some/file2"))?.is_some());
1701 assert!(map.get(p(b"some/file2"))?.is_some());
1704
1702
1705 map.set_untracked(p(b"some/file3"))?;
1703 map.set_untracked(p(b"some/file3"))?;
1706 assert_eq!(map.len(), 5);
1704 assert_eq!(map.len(), 5);
1707 assert_eq!(tracked_descendants(&map, b"some"), 0);
1705 assert_eq!(tracked_descendants(&map, b"some"), 0);
1708 assert!(map.get(p(b"some/file3"))?.is_some());
1706 assert!(map.get(p(b"some/file3"))?.is_some());
1709
1707
1710 Ok(())
1708 Ok(())
1711 }
1709 }
1712
1710
1713 /// Check that copies counter is correctly updated
1711 /// Check that copies counter is correctly updated
1714 #[test]
1712 #[test]
1715 fn test_copy_source() -> Result<(), DirstateError> {
1713 fn test_copy_source() -> Result<(), DirstateError> {
1716 let mut map = OwningDirstateMap::new_empty(vec![]);
1714 let mut map = OwningDirstateMap::new_empty(vec![]);
1717
1715
1718 // Clean file
1716 // Clean file
1719 map.reset_state(p(b"files/clean"), true, true, false, false, None)?;
1717 map.reset_state(p(b"files/clean"), true, true, false, false, None)?;
1720 // Merged file
1718 // Merged file
1721 map.reset_state(p(b"files/from_p2"), true, true, true, false, None)?;
1719 map.reset_state(p(b"files/from_p2"), true, true, true, false, None)?;
1722 // Removed file
1720 // Removed file
1723 map.reset_state(p(b"removed"), false, true, false, false, None)?;
1721 map.reset_state(p(b"removed"), false, true, false, false, None)?;
1724 // Added file
1722 // Added file
1725 map.reset_state(p(b"files/added"), true, false, false, false, None)?;
1723 map.reset_state(p(b"files/added"), true, false, false, false, None)?;
1726 // Add copy
1724 // Add copy
1727 map.copy_map_insert(p(b"files/clean"), p(b"clean_copy_source"))?;
1725 map.copy_map_insert(p(b"files/clean"), p(b"clean_copy_source"))?;
1728 assert_eq!(map.copy_map_len(), 1);
1726 assert_eq!(map.copy_map_len(), 1);
1729
1727
1730 // Copy override
1728 // Copy override
1731 map.copy_map_insert(p(b"files/clean"), p(b"other_clean_copy_source"))?;
1729 map.copy_map_insert(p(b"files/clean"), p(b"other_clean_copy_source"))?;
1732 assert_eq!(map.copy_map_len(), 1);
1730 assert_eq!(map.copy_map_len(), 1);
1733
1731
1734 // Multiple copies
1732 // Multiple copies
1735 map.copy_map_insert(p(b"removed"), p(b"removed_copy_source"))?;
1733 map.copy_map_insert(p(b"removed"), p(b"removed_copy_source"))?;
1736 assert_eq!(map.copy_map_len(), 2);
1734 assert_eq!(map.copy_map_len(), 2);
1737
1735
1738 map.copy_map_insert(p(b"files/added"), p(b"added_copy_source"))?;
1736 map.copy_map_insert(p(b"files/added"), p(b"added_copy_source"))?;
1739 assert_eq!(map.copy_map_len(), 3);
1737 assert_eq!(map.copy_map_len(), 3);
1740
1738
1741 // Added, so the entry is completely removed
1739 // Added, so the entry is completely removed
1742 map.set_untracked(p(b"files/added"))?;
1740 map.set_untracked(p(b"files/added"))?;
1743 assert_does_not_exist(&map, b"files/added");
1741 assert_does_not_exist(&map, b"files/added");
1744 assert_eq!(map.copy_map_len(), 2);
1742 assert_eq!(map.copy_map_len(), 2);
1745
1743
1746 // Removed, so the entry is kept around, so is its copy
1744 // Removed, so the entry is kept around, so is its copy
1747 map.set_untracked(p(b"removed"))?;
1745 map.set_untracked(p(b"removed"))?;
1748 assert!(map.get(p(b"removed"))?.is_some());
1746 assert!(map.get(p(b"removed"))?.is_some());
1749 assert_eq!(map.copy_map_len(), 2);
1747 assert_eq!(map.copy_map_len(), 2);
1750
1748
1751 // Clean, so the entry is kept around, but not its copy
1749 // Clean, so the entry is kept around, but not its copy
1752 map.set_untracked(p(b"files/clean"))?;
1750 map.set_untracked(p(b"files/clean"))?;
1753 assert!(map.get(p(b"files/clean"))?.is_some());
1751 assert!(map.get(p(b"files/clean"))?.is_some());
1754 assert_eq!(map.copy_map_len(), 1);
1752 assert_eq!(map.copy_map_len(), 1);
1755
1753
1756 map.copy_map_insert(p(b"files/from_p2"), p(b"from_p2_copy_source"))?;
1754 map.copy_map_insert(p(b"files/from_p2"), p(b"from_p2_copy_source"))?;
1757 assert_eq!(map.copy_map_len(), 2);
1755 assert_eq!(map.copy_map_len(), 2);
1758
1756
1759 // Info from p2, so its copy source info is kept around
1757 // Info from p2, so its copy source info is kept around
1760 map.set_untracked(p(b"files/from_p2"))?;
1758 map.set_untracked(p(b"files/from_p2"))?;
1761 assert!(map.get(p(b"files/from_p2"))?.is_some());
1759 assert!(map.get(p(b"files/from_p2"))?.is_some());
1762 assert_eq!(map.copy_map_len(), 2);
1760 assert_eq!(map.copy_map_len(), 2);
1763
1761
1764 Ok(())
1762 Ok(())
1765 }
1763 }
1766
1764
1767 /// Test with "on disk" data. For the sake of this test, the "on disk" data
1765 /// Test with "on disk" data. For the sake of this test, the "on disk" data
1768 /// does not actually come from the disk, but it's opaque to the code being
1766 /// does not actually come from the disk, but it's opaque to the code being
1769 /// tested.
1767 /// tested.
1770 #[test]
1768 #[test]
1771 fn test_on_disk() -> Result<(), DirstateError> {
1769 fn test_on_disk() -> Result<(), DirstateError> {
1772 // First let's create some data to put "on disk"
1770 // First let's create some data to put "on disk"
1773 let mut map = OwningDirstateMap::new_empty(vec![]);
1771 let mut map = OwningDirstateMap::new_empty(vec![]);
1774
1772
1775 // A file that was just added
1773 // A file that was just added
1776 map.set_tracked(p(b"some/nested/added"))?;
1774 map.set_tracked(p(b"some/nested/added"))?;
1777 map.copy_map_insert(p(b"some/nested/added"), p(b"added_copy_source"))?;
1775 map.copy_map_insert(p(b"some/nested/added"), p(b"added_copy_source"))?;
1778
1776
1779 // A file that was removed
1777 // A file that was removed
1780 map.reset_state(
1778 map.reset_state(
1781 p(b"some/nested/removed"),
1779 p(b"some/nested/removed"),
1782 false,
1780 false,
1783 true,
1781 true,
1784 false,
1782 false,
1785 false,
1783 false,
1786 None,
1784 None,
1787 )?;
1785 )?;
1788 // Only present in p2
1786 // Only present in p2
1789 map.reset_state(
1787 map.reset_state(
1790 p(b"other/p2_info_only"),
1788 p(b"other/p2_info_only"),
1791 false,
1789 false,
1792 false,
1790 false,
1793 true,
1791 true,
1794 false,
1792 false,
1795 None,
1793 None,
1796 )?;
1794 )?;
1797 map.copy_map_insert(
1795 map.copy_map_insert(
1798 p(b"other/p2_info_only"),
1796 p(b"other/p2_info_only"),
1799 p(b"other/p2_info_copy_source"),
1797 p(b"other/p2_info_copy_source"),
1800 )?;
1798 )?;
1801 // A file that was merged
1799 // A file that was merged
1802 map.reset_state(p(b"merged"), true, true, true, false, None)?;
1800 map.reset_state(p(b"merged"), true, true, true, false, None)?;
1803 // A file that is added, with info from p2
1801 // A file that is added, with info from p2
1804 // XXX is that actually possible?
1802 // XXX is that actually possible?
1805 map.reset_state(
1803 map.reset_state(
1806 p(b"other/added_with_p2"),
1804 p(b"other/added_with_p2"),
1807 true,
1805 true,
1808 false,
1806 false,
1809 true,
1807 true,
1810 false,
1808 false,
1811 None,
1809 None,
1812 )?;
1810 )?;
1813 // One layer without any files to test deletion cascade
1811 // One layer without any files to test deletion cascade
1814 // A clean file
1812 // A clean file
1815 map.reset_state(
1813 map.reset_state(
1816 p(b"some/other/nested/clean"),
1814 p(b"some/other/nested/clean"),
1817 true,
1815 true,
1818 true,
1816 true,
1819 false,
1817 false,
1820 false,
1818 false,
1821 None,
1819 None,
1822 )?;
1820 )?;
1823
1821
1824 let (packed, metadata, _should_append, _old_data_size) =
1822 let (packed, metadata, _should_append, _old_data_size) =
1825 map.pack_v2(false)?;
1823 map.pack_v2(false)?;
1826 let packed_len = packed.len();
1824 let packed_len = packed.len();
1827 assert!(packed_len > 0);
1825 assert!(packed_len > 0);
1828
1826
1829 // Recreate "from disk"
1827 // Recreate "from disk"
1830 let mut map = OwningDirstateMap::new_v2(
1828 let mut map = OwningDirstateMap::new_v2(
1831 packed,
1829 packed,
1832 packed_len,
1830 packed_len,
1833 metadata.as_bytes(),
1831 metadata.as_bytes(),
1834 )?;
1832 )?;
1835
1833
1836 // Check that everything is accounted for
1834 // Check that everything is accounted for
1837 assert!(map.contains_key(p(b"some/nested/added"))?);
1835 assert!(map.contains_key(p(b"some/nested/added"))?);
1838 assert!(map.contains_key(p(b"some/nested/removed"))?);
1836 assert!(map.contains_key(p(b"some/nested/removed"))?);
1839 assert!(map.contains_key(p(b"merged"))?);
1837 assert!(map.contains_key(p(b"merged"))?);
1840 assert!(map.contains_key(p(b"other/p2_info_only"))?);
1838 assert!(map.contains_key(p(b"other/p2_info_only"))?);
1841 assert!(map.contains_key(p(b"other/added_with_p2"))?);
1839 assert!(map.contains_key(p(b"other/added_with_p2"))?);
1842 assert!(map.contains_key(p(b"some/other/nested/clean"))?);
1840 assert!(map.contains_key(p(b"some/other/nested/clean"))?);
1843 assert_eq!(
1841 assert_eq!(
1844 map.copy_map_get(p(b"some/nested/added"))?,
1842 map.copy_map_get(p(b"some/nested/added"))?,
1845 Some(p(b"added_copy_source"))
1843 Some(p(b"added_copy_source"))
1846 );
1844 );
1847 assert_eq!(
1845 assert_eq!(
1848 map.copy_map_get(p(b"other/p2_info_only"))?,
1846 map.copy_map_get(p(b"other/p2_info_only"))?,
1849 Some(p(b"other/p2_info_copy_source"))
1847 Some(p(b"other/p2_info_copy_source"))
1850 );
1848 );
1851 assert_eq!(tracked_descendants(&map, b"some"), 2);
1849 assert_eq!(tracked_descendants(&map, b"some"), 2);
1852 assert_eq!(descendants_with_an_entry(&map, b"some"), 3);
1850 assert_eq!(descendants_with_an_entry(&map, b"some"), 3);
1853 assert_eq!(tracked_descendants(&map, b"other"), 1);
1851 assert_eq!(tracked_descendants(&map, b"other"), 1);
1854 assert_eq!(descendants_with_an_entry(&map, b"other"), 2);
1852 assert_eq!(descendants_with_an_entry(&map, b"other"), 2);
1855 assert_eq!(tracked_descendants(&map, b"some/other"), 1);
1853 assert_eq!(tracked_descendants(&map, b"some/other"), 1);
1856 assert_eq!(descendants_with_an_entry(&map, b"some/other"), 1);
1854 assert_eq!(descendants_with_an_entry(&map, b"some/other"), 1);
1857 assert_eq!(tracked_descendants(&map, b"some/other/nested"), 1);
1855 assert_eq!(tracked_descendants(&map, b"some/other/nested"), 1);
1858 assert_eq!(descendants_with_an_entry(&map, b"some/other/nested"), 1);
1856 assert_eq!(descendants_with_an_entry(&map, b"some/other/nested"), 1);
1859 assert_eq!(tracked_descendants(&map, b"some/nested"), 1);
1857 assert_eq!(tracked_descendants(&map, b"some/nested"), 1);
1860 assert_eq!(descendants_with_an_entry(&map, b"some/nested"), 2);
1858 assert_eq!(descendants_with_an_entry(&map, b"some/nested"), 2);
1861 assert_eq!(map.len(), 6);
1859 assert_eq!(map.len(), 6);
1862 assert_eq!(map.get_map().unreachable_bytes, 0);
1860 assert_eq!(map.get_map().unreachable_bytes, 0);
1863 assert_eq!(map.copy_map_len(), 2);
1861 assert_eq!(map.copy_map_len(), 2);
1864
1862
1865 // Shouldn't change anything since it's already not tracked
1863 // Shouldn't change anything since it's already not tracked
1866 map.set_untracked(p(b"some/nested/removed"))?;
1864 map.set_untracked(p(b"some/nested/removed"))?;
1867 assert_eq!(map.get_map().unreachable_bytes, 0);
1865 assert_eq!(map.get_map().unreachable_bytes, 0);
1868
1866
1869 match map.get_map().root {
1867 match map.get_map().root {
1870 ChildNodes::InMemory(_) => {
1868 ChildNodes::InMemory(_) => {
1871 panic!("root should not have been mutated")
1869 panic!("root should not have been mutated")
1872 }
1870 }
1873 _ => (),
1871 _ => (),
1874 }
1872 }
1875 // We haven't mutated enough (nothing, actually), we should still be in
1873 // We haven't mutated enough (nothing, actually), we should still be in
1876 // the append strategy
1874 // the append strategy
1877 assert!(map.get_map().write_should_append());
1875 assert!(map.get_map().write_should_append());
1878
1876
1879 // But this mutates the structure, so there should be unreachable_bytes
1877 // But this mutates the structure, so there should be unreachable_bytes
1880 assert!(map.set_untracked(p(b"some/nested/added"))?);
1878 assert!(map.set_untracked(p(b"some/nested/added"))?);
1881 let unreachable_bytes = map.get_map().unreachable_bytes;
1879 let unreachable_bytes = map.get_map().unreachable_bytes;
1882 assert!(unreachable_bytes > 0);
1880 assert!(unreachable_bytes > 0);
1883
1881
1884 match map.get_map().root {
1882 match map.get_map().root {
1885 ChildNodes::OnDisk(_) => panic!("root should have been mutated"),
1883 ChildNodes::OnDisk(_) => panic!("root should have been mutated"),
1886 _ => (),
1884 _ => (),
1887 }
1885 }
1888
1886
1889 // This should not mutate the structure either, since `root` has
1887 // This should not mutate the structure either, since `root` has
1890 // already been mutated along with its direct children.
1888 // already been mutated along with its direct children.
1891 map.set_untracked(p(b"merged"))?;
1889 map.set_untracked(p(b"merged"))?;
1892 assert_eq!(map.get_map().unreachable_bytes, unreachable_bytes);
1890 assert_eq!(map.get_map().unreachable_bytes, unreachable_bytes);
1893
1891
1894 match map.get_map().get_node(p(b"other/added_with_p2"))?.unwrap() {
1892 match map.get_map().get_node(p(b"other/added_with_p2"))?.unwrap() {
1895 NodeRef::InMemory(_, _) => {
1893 NodeRef::InMemory(_, _) => {
1896 panic!("'other/added_with_p2' should not have been mutated")
1894 panic!("'other/added_with_p2' should not have been mutated")
1897 }
1895 }
1898 _ => (),
1896 _ => (),
1899 }
1897 }
1900 // But this should, since it's in a different path
1898 // But this should, since it's in a different path
1901 // than `<root>some/nested/add`
1899 // than `<root>some/nested/add`
1902 map.set_untracked(p(b"other/added_with_p2"))?;
1900 map.set_untracked(p(b"other/added_with_p2"))?;
1903 assert!(map.get_map().unreachable_bytes > unreachable_bytes);
1901 assert!(map.get_map().unreachable_bytes > unreachable_bytes);
1904
1902
1905 match map.get_map().get_node(p(b"other/added_with_p2"))?.unwrap() {
1903 match map.get_map().get_node(p(b"other/added_with_p2"))?.unwrap() {
1906 NodeRef::OnDisk(_) => {
1904 NodeRef::OnDisk(_) => {
1907 panic!("'other/added_with_p2' should have been mutated")
1905 panic!("'other/added_with_p2' should have been mutated")
1908 }
1906 }
1909 _ => (),
1907 _ => (),
1910 }
1908 }
1911
1909
1912 // We have rewritten most of the tree, we should create a new file
1910 // We have rewritten most of the tree, we should create a new file
1913 assert!(!map.get_map().write_should_append());
1911 assert!(!map.get_map().write_should_append());
1914
1912
1915 Ok(())
1913 Ok(())
1916 }
1914 }
1917 }
1915 }
General Comments 0
You need to be logged in to leave comments. Login now