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