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