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