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