##// END OF EJS Templates
rust-dirstatemap: use `DirstateEntry::tracked` directly...
Raphaël Gomès -
r50014:afe60def default
parent child Browse files
Show More
@@ -1,1428 +1,1426 b''
1 use bytes_cast::BytesCast;
1 use bytes_cast::BytesCast;
2 use micro_timer::timed;
2 use micro_timer::timed;
3 use std::borrow::Cow;
3 use std::borrow::Cow;
4 use std::path::PathBuf;
4 use std::path::PathBuf;
5
5
6 use super::on_disk;
6 use super::on_disk;
7 use super::on_disk::DirstateV2ParseError;
7 use super::on_disk::DirstateV2ParseError;
8 use super::owning::OwningDirstateMap;
8 use super::owning::OwningDirstateMap;
9 use super::path_with_basename::WithBasename;
9 use super::path_with_basename::WithBasename;
10 use crate::dirstate::parsers::pack_entry;
10 use crate::dirstate::parsers::pack_entry;
11 use crate::dirstate::parsers::packed_entry_size;
11 use crate::dirstate::parsers::packed_entry_size;
12 use crate::dirstate::parsers::parse_dirstate_entries;
12 use crate::dirstate::parsers::parse_dirstate_entries;
13 use crate::dirstate::CopyMapIter;
13 use crate::dirstate::CopyMapIter;
14 use crate::dirstate::DirstateV2Data;
14 use crate::dirstate::DirstateV2Data;
15 use crate::dirstate::ParentFileData;
15 use crate::dirstate::ParentFileData;
16 use crate::dirstate::StateMapIter;
16 use crate::dirstate::StateMapIter;
17 use crate::dirstate::TruncatedTimestamp;
17 use crate::dirstate::TruncatedTimestamp;
18 use crate::matchers::Matcher;
18 use crate::matchers::Matcher;
19 use crate::utils::hg_path::{HgPath, HgPathBuf};
19 use crate::utils::hg_path::{HgPath, HgPathBuf};
20 use crate::DirstateEntry;
20 use crate::DirstateEntry;
21 use crate::DirstateError;
21 use crate::DirstateError;
22 use crate::DirstateMapError;
22 use crate::DirstateMapError;
23 use crate::DirstateParents;
23 use crate::DirstateParents;
24 use crate::DirstateStatus;
24 use crate::DirstateStatus;
25 use crate::EntryState;
25 use crate::EntryState;
26 use crate::FastHashbrownMap as FastHashMap;
26 use crate::FastHashbrownMap as FastHashMap;
27 use crate::PatternFileWarning;
27 use crate::PatternFileWarning;
28 use crate::StatusError;
28 use crate::StatusError;
29 use crate::StatusOptions;
29 use crate::StatusOptions;
30
30
31 /// Append to an existing data file if the amount of unreachable data (not used
31 /// Append to an existing data file if the amount of unreachable data (not used
32 /// anymore) is less than this fraction of the total amount of existing data.
32 /// anymore) is less than this fraction of the total amount of existing data.
33 const ACCEPTABLE_UNREACHABLE_BYTES_RATIO: f32 = 0.5;
33 const ACCEPTABLE_UNREACHABLE_BYTES_RATIO: f32 = 0.5;
34
34
35 pub struct DirstateMap<'on_disk> {
35 pub struct DirstateMap<'on_disk> {
36 /// Contents of the `.hg/dirstate` file
36 /// Contents of the `.hg/dirstate` file
37 pub(super) on_disk: &'on_disk [u8],
37 pub(super) on_disk: &'on_disk [u8],
38
38
39 pub(super) root: ChildNodes<'on_disk>,
39 pub(super) root: ChildNodes<'on_disk>,
40
40
41 /// 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()`.
42 pub(super) nodes_with_entry_count: u32,
42 pub(super) nodes_with_entry_count: u32,
43
43
44 /// Number of nodes anywhere in the tree that have
44 /// Number of nodes anywhere in the tree that have
45 /// `.copy_source.is_some()`.
45 /// `.copy_source.is_some()`.
46 pub(super) nodes_with_copy_source_count: u32,
46 pub(super) nodes_with_copy_source_count: u32,
47
47
48 /// See on_disk::Header
48 /// See on_disk::Header
49 pub(super) ignore_patterns_hash: on_disk::IgnorePatternsHash,
49 pub(super) ignore_patterns_hash: on_disk::IgnorePatternsHash,
50
50
51 /// How many bytes of `on_disk` are not used anymore
51 /// How many bytes of `on_disk` are not used anymore
52 pub(super) unreachable_bytes: u32,
52 pub(super) unreachable_bytes: u32,
53 }
53 }
54
54
55 /// 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
56 /// 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
57 /// 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
58 /// names. However `HashMap` would waste time always re-hashing the same
58 /// names. However `HashMap` would waste time always re-hashing the same
59 /// string prefix.
59 /// string prefix.
60 pub(super) type NodeKey<'on_disk> = WithBasename<Cow<'on_disk, HgPath>>;
60 pub(super) type NodeKey<'on_disk> = WithBasename<Cow<'on_disk, HgPath>>;
61
61
62 /// 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
63 /// 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.
64 pub(super) enum BorrowedPath<'tree, 'on_disk> {
64 pub(super) enum BorrowedPath<'tree, 'on_disk> {
65 InMemory(&'tree HgPathBuf),
65 InMemory(&'tree HgPathBuf),
66 OnDisk(&'on_disk HgPath),
66 OnDisk(&'on_disk HgPath),
67 }
67 }
68
68
69 pub(super) enum ChildNodes<'on_disk> {
69 pub(super) enum ChildNodes<'on_disk> {
70 InMemory(FastHashMap<NodeKey<'on_disk>, Node<'on_disk>>),
70 InMemory(FastHashMap<NodeKey<'on_disk>, Node<'on_disk>>),
71 OnDisk(&'on_disk [on_disk::Node]),
71 OnDisk(&'on_disk [on_disk::Node]),
72 }
72 }
73
73
74 pub(super) enum ChildNodesRef<'tree, 'on_disk> {
74 pub(super) enum ChildNodesRef<'tree, 'on_disk> {
75 InMemory(&'tree FastHashMap<NodeKey<'on_disk>, Node<'on_disk>>),
75 InMemory(&'tree FastHashMap<NodeKey<'on_disk>, Node<'on_disk>>),
76 OnDisk(&'on_disk [on_disk::Node]),
76 OnDisk(&'on_disk [on_disk::Node]),
77 }
77 }
78
78
79 pub(super) enum NodeRef<'tree, 'on_disk> {
79 pub(super) enum NodeRef<'tree, 'on_disk> {
80 InMemory(&'tree NodeKey<'on_disk>, &'tree Node<'on_disk>),
80 InMemory(&'tree NodeKey<'on_disk>, &'tree Node<'on_disk>),
81 OnDisk(&'on_disk on_disk::Node),
81 OnDisk(&'on_disk on_disk::Node),
82 }
82 }
83
83
84 impl<'tree, 'on_disk> BorrowedPath<'tree, 'on_disk> {
84 impl<'tree, 'on_disk> BorrowedPath<'tree, 'on_disk> {
85 pub fn detach_from_tree(&self) -> Cow<'on_disk, HgPath> {
85 pub fn detach_from_tree(&self) -> Cow<'on_disk, HgPath> {
86 match *self {
86 match *self {
87 BorrowedPath::InMemory(in_memory) => Cow::Owned(in_memory.clone()),
87 BorrowedPath::InMemory(in_memory) => Cow::Owned(in_memory.clone()),
88 BorrowedPath::OnDisk(on_disk) => Cow::Borrowed(on_disk),
88 BorrowedPath::OnDisk(on_disk) => Cow::Borrowed(on_disk),
89 }
89 }
90 }
90 }
91 }
91 }
92
92
93 impl<'tree, 'on_disk> std::ops::Deref for BorrowedPath<'tree, 'on_disk> {
93 impl<'tree, 'on_disk> std::ops::Deref for BorrowedPath<'tree, 'on_disk> {
94 type Target = HgPath;
94 type Target = HgPath;
95
95
96 fn deref(&self) -> &HgPath {
96 fn deref(&self) -> &HgPath {
97 match *self {
97 match *self {
98 BorrowedPath::InMemory(in_memory) => in_memory,
98 BorrowedPath::InMemory(in_memory) => in_memory,
99 BorrowedPath::OnDisk(on_disk) => on_disk,
99 BorrowedPath::OnDisk(on_disk) => on_disk,
100 }
100 }
101 }
101 }
102 }
102 }
103
103
104 impl Default for ChildNodes<'_> {
104 impl Default for ChildNodes<'_> {
105 fn default() -> Self {
105 fn default() -> Self {
106 ChildNodes::InMemory(Default::default())
106 ChildNodes::InMemory(Default::default())
107 }
107 }
108 }
108 }
109
109
110 impl<'on_disk> ChildNodes<'on_disk> {
110 impl<'on_disk> ChildNodes<'on_disk> {
111 pub(super) fn as_ref<'tree>(
111 pub(super) fn as_ref<'tree>(
112 &'tree self,
112 &'tree self,
113 ) -> ChildNodesRef<'tree, 'on_disk> {
113 ) -> ChildNodesRef<'tree, 'on_disk> {
114 match self {
114 match self {
115 ChildNodes::InMemory(nodes) => ChildNodesRef::InMemory(nodes),
115 ChildNodes::InMemory(nodes) => ChildNodesRef::InMemory(nodes),
116 ChildNodes::OnDisk(nodes) => ChildNodesRef::OnDisk(nodes),
116 ChildNodes::OnDisk(nodes) => ChildNodesRef::OnDisk(nodes),
117 }
117 }
118 }
118 }
119
119
120 pub(super) fn is_empty(&self) -> bool {
120 pub(super) fn is_empty(&self) -> bool {
121 match self {
121 match self {
122 ChildNodes::InMemory(nodes) => nodes.is_empty(),
122 ChildNodes::InMemory(nodes) => nodes.is_empty(),
123 ChildNodes::OnDisk(nodes) => nodes.is_empty(),
123 ChildNodes::OnDisk(nodes) => nodes.is_empty(),
124 }
124 }
125 }
125 }
126
126
127 fn make_mut(
127 fn make_mut(
128 &mut self,
128 &mut self,
129 on_disk: &'on_disk [u8],
129 on_disk: &'on_disk [u8],
130 unreachable_bytes: &mut u32,
130 unreachable_bytes: &mut u32,
131 ) -> Result<
131 ) -> Result<
132 &mut FastHashMap<NodeKey<'on_disk>, Node<'on_disk>>,
132 &mut FastHashMap<NodeKey<'on_disk>, Node<'on_disk>>,
133 DirstateV2ParseError,
133 DirstateV2ParseError,
134 > {
134 > {
135 match self {
135 match self {
136 ChildNodes::InMemory(nodes) => Ok(nodes),
136 ChildNodes::InMemory(nodes) => Ok(nodes),
137 ChildNodes::OnDisk(nodes) => {
137 ChildNodes::OnDisk(nodes) => {
138 *unreachable_bytes +=
138 *unreachable_bytes +=
139 std::mem::size_of_val::<[on_disk::Node]>(nodes) as u32;
139 std::mem::size_of_val::<[on_disk::Node]>(nodes) as u32;
140 let nodes = nodes
140 let nodes = nodes
141 .iter()
141 .iter()
142 .map(|node| {
142 .map(|node| {
143 Ok((
143 Ok((
144 node.path(on_disk)?,
144 node.path(on_disk)?,
145 node.to_in_memory_node(on_disk)?,
145 node.to_in_memory_node(on_disk)?,
146 ))
146 ))
147 })
147 })
148 .collect::<Result<_, _>>()?;
148 .collect::<Result<_, _>>()?;
149 *self = ChildNodes::InMemory(nodes);
149 *self = ChildNodes::InMemory(nodes);
150 match self {
150 match self {
151 ChildNodes::InMemory(nodes) => Ok(nodes),
151 ChildNodes::InMemory(nodes) => Ok(nodes),
152 ChildNodes::OnDisk(_) => unreachable!(),
152 ChildNodes::OnDisk(_) => unreachable!(),
153 }
153 }
154 }
154 }
155 }
155 }
156 }
156 }
157 }
157 }
158
158
159 impl<'tree, 'on_disk> ChildNodesRef<'tree, 'on_disk> {
159 impl<'tree, 'on_disk> ChildNodesRef<'tree, 'on_disk> {
160 pub(super) fn get(
160 pub(super) fn get(
161 &self,
161 &self,
162 base_name: &HgPath,
162 base_name: &HgPath,
163 on_disk: &'on_disk [u8],
163 on_disk: &'on_disk [u8],
164 ) -> Result<Option<NodeRef<'tree, 'on_disk>>, DirstateV2ParseError> {
164 ) -> Result<Option<NodeRef<'tree, 'on_disk>>, DirstateV2ParseError> {
165 match self {
165 match self {
166 ChildNodesRef::InMemory(nodes) => Ok(nodes
166 ChildNodesRef::InMemory(nodes) => Ok(nodes
167 .get_key_value(base_name)
167 .get_key_value(base_name)
168 .map(|(k, v)| NodeRef::InMemory(k, v))),
168 .map(|(k, v)| NodeRef::InMemory(k, v))),
169 ChildNodesRef::OnDisk(nodes) => {
169 ChildNodesRef::OnDisk(nodes) => {
170 let mut parse_result = Ok(());
170 let mut parse_result = Ok(());
171 let search_result = nodes.binary_search_by(|node| {
171 let search_result = nodes.binary_search_by(|node| {
172 match node.base_name(on_disk) {
172 match node.base_name(on_disk) {
173 Ok(node_base_name) => node_base_name.cmp(base_name),
173 Ok(node_base_name) => node_base_name.cmp(base_name),
174 Err(e) => {
174 Err(e) => {
175 parse_result = Err(e);
175 parse_result = Err(e);
176 // Dummy comparison result, `search_result` won’t
176 // Dummy comparison result, `search_result` won’t
177 // be used since `parse_result` is an error
177 // be used since `parse_result` is an error
178 std::cmp::Ordering::Equal
178 std::cmp::Ordering::Equal
179 }
179 }
180 }
180 }
181 });
181 });
182 parse_result.map(|()| {
182 parse_result.map(|()| {
183 search_result.ok().map(|i| NodeRef::OnDisk(&nodes[i]))
183 search_result.ok().map(|i| NodeRef::OnDisk(&nodes[i]))
184 })
184 })
185 }
185 }
186 }
186 }
187 }
187 }
188
188
189 /// Iterate in undefined order
189 /// Iterate in undefined order
190 pub(super) fn iter(
190 pub(super) fn iter(
191 &self,
191 &self,
192 ) -> impl Iterator<Item = NodeRef<'tree, 'on_disk>> {
192 ) -> impl Iterator<Item = NodeRef<'tree, 'on_disk>> {
193 match self {
193 match self {
194 ChildNodesRef::InMemory(nodes) => itertools::Either::Left(
194 ChildNodesRef::InMemory(nodes) => itertools::Either::Left(
195 nodes.iter().map(|(k, v)| NodeRef::InMemory(k, v)),
195 nodes.iter().map(|(k, v)| NodeRef::InMemory(k, v)),
196 ),
196 ),
197 ChildNodesRef::OnDisk(nodes) => {
197 ChildNodesRef::OnDisk(nodes) => {
198 itertools::Either::Right(nodes.iter().map(NodeRef::OnDisk))
198 itertools::Either::Right(nodes.iter().map(NodeRef::OnDisk))
199 }
199 }
200 }
200 }
201 }
201 }
202
202
203 /// Iterate in parallel in undefined order
203 /// Iterate in parallel in undefined order
204 pub(super) fn par_iter(
204 pub(super) fn par_iter(
205 &self,
205 &self,
206 ) -> impl rayon::iter::ParallelIterator<Item = NodeRef<'tree, 'on_disk>>
206 ) -> impl rayon::iter::ParallelIterator<Item = NodeRef<'tree, 'on_disk>>
207 {
207 {
208 use rayon::prelude::*;
208 use rayon::prelude::*;
209 match self {
209 match self {
210 ChildNodesRef::InMemory(nodes) => rayon::iter::Either::Left(
210 ChildNodesRef::InMemory(nodes) => rayon::iter::Either::Left(
211 nodes.par_iter().map(|(k, v)| NodeRef::InMemory(k, v)),
211 nodes.par_iter().map(|(k, v)| NodeRef::InMemory(k, v)),
212 ),
212 ),
213 ChildNodesRef::OnDisk(nodes) => rayon::iter::Either::Right(
213 ChildNodesRef::OnDisk(nodes) => rayon::iter::Either::Right(
214 nodes.par_iter().map(NodeRef::OnDisk),
214 nodes.par_iter().map(NodeRef::OnDisk),
215 ),
215 ),
216 }
216 }
217 }
217 }
218
218
219 pub(super) fn sorted(&self) -> Vec<NodeRef<'tree, 'on_disk>> {
219 pub(super) fn sorted(&self) -> Vec<NodeRef<'tree, 'on_disk>> {
220 match self {
220 match self {
221 ChildNodesRef::InMemory(nodes) => {
221 ChildNodesRef::InMemory(nodes) => {
222 let mut vec: Vec<_> = nodes
222 let mut vec: Vec<_> = nodes
223 .iter()
223 .iter()
224 .map(|(k, v)| NodeRef::InMemory(k, v))
224 .map(|(k, v)| NodeRef::InMemory(k, v))
225 .collect();
225 .collect();
226 fn sort_key<'a>(node: &'a NodeRef) -> &'a HgPath {
226 fn sort_key<'a>(node: &'a NodeRef) -> &'a HgPath {
227 match node {
227 match node {
228 NodeRef::InMemory(path, _node) => path.base_name(),
228 NodeRef::InMemory(path, _node) => path.base_name(),
229 NodeRef::OnDisk(_) => unreachable!(),
229 NodeRef::OnDisk(_) => unreachable!(),
230 }
230 }
231 }
231 }
232 // `sort_unstable_by_key` doesn’t allow keys borrowing from the
232 // `sort_unstable_by_key` doesn’t allow keys borrowing from the
233 // value: https://github.com/rust-lang/rust/issues/34162
233 // value: https://github.com/rust-lang/rust/issues/34162
234 vec.sort_unstable_by(|a, b| sort_key(a).cmp(sort_key(b)));
234 vec.sort_unstable_by(|a, b| sort_key(a).cmp(sort_key(b)));
235 vec
235 vec
236 }
236 }
237 ChildNodesRef::OnDisk(nodes) => {
237 ChildNodesRef::OnDisk(nodes) => {
238 // Nodes on disk are already sorted
238 // Nodes on disk are already sorted
239 nodes.iter().map(NodeRef::OnDisk).collect()
239 nodes.iter().map(NodeRef::OnDisk).collect()
240 }
240 }
241 }
241 }
242 }
242 }
243 }
243 }
244
244
245 impl<'tree, 'on_disk> NodeRef<'tree, 'on_disk> {
245 impl<'tree, 'on_disk> NodeRef<'tree, 'on_disk> {
246 pub(super) fn full_path(
246 pub(super) fn full_path(
247 &self,
247 &self,
248 on_disk: &'on_disk [u8],
248 on_disk: &'on_disk [u8],
249 ) -> Result<&'tree HgPath, DirstateV2ParseError> {
249 ) -> Result<&'tree HgPath, DirstateV2ParseError> {
250 match self {
250 match self {
251 NodeRef::InMemory(path, _node) => Ok(path.full_path()),
251 NodeRef::InMemory(path, _node) => Ok(path.full_path()),
252 NodeRef::OnDisk(node) => node.full_path(on_disk),
252 NodeRef::OnDisk(node) => node.full_path(on_disk),
253 }
253 }
254 }
254 }
255
255
256 /// Returns a `BorrowedPath`, which can be turned into a `Cow<'on_disk,
256 /// Returns a `BorrowedPath`, which can be turned into a `Cow<'on_disk,
257 /// HgPath>` detached from `'tree`
257 /// HgPath>` detached from `'tree`
258 pub(super) fn full_path_borrowed(
258 pub(super) fn full_path_borrowed(
259 &self,
259 &self,
260 on_disk: &'on_disk [u8],
260 on_disk: &'on_disk [u8],
261 ) -> Result<BorrowedPath<'tree, 'on_disk>, DirstateV2ParseError> {
261 ) -> Result<BorrowedPath<'tree, 'on_disk>, DirstateV2ParseError> {
262 match self {
262 match self {
263 NodeRef::InMemory(path, _node) => match path.full_path() {
263 NodeRef::InMemory(path, _node) => match path.full_path() {
264 Cow::Borrowed(on_disk) => Ok(BorrowedPath::OnDisk(on_disk)),
264 Cow::Borrowed(on_disk) => Ok(BorrowedPath::OnDisk(on_disk)),
265 Cow::Owned(in_memory) => Ok(BorrowedPath::InMemory(in_memory)),
265 Cow::Owned(in_memory) => Ok(BorrowedPath::InMemory(in_memory)),
266 },
266 },
267 NodeRef::OnDisk(node) => {
267 NodeRef::OnDisk(node) => {
268 Ok(BorrowedPath::OnDisk(node.full_path(on_disk)?))
268 Ok(BorrowedPath::OnDisk(node.full_path(on_disk)?))
269 }
269 }
270 }
270 }
271 }
271 }
272
272
273 pub(super) fn base_name(
273 pub(super) fn base_name(
274 &self,
274 &self,
275 on_disk: &'on_disk [u8],
275 on_disk: &'on_disk [u8],
276 ) -> Result<&'tree HgPath, DirstateV2ParseError> {
276 ) -> Result<&'tree HgPath, DirstateV2ParseError> {
277 match self {
277 match self {
278 NodeRef::InMemory(path, _node) => Ok(path.base_name()),
278 NodeRef::InMemory(path, _node) => Ok(path.base_name()),
279 NodeRef::OnDisk(node) => node.base_name(on_disk),
279 NodeRef::OnDisk(node) => node.base_name(on_disk),
280 }
280 }
281 }
281 }
282
282
283 pub(super) fn children(
283 pub(super) fn children(
284 &self,
284 &self,
285 on_disk: &'on_disk [u8],
285 on_disk: &'on_disk [u8],
286 ) -> Result<ChildNodesRef<'tree, 'on_disk>, DirstateV2ParseError> {
286 ) -> Result<ChildNodesRef<'tree, 'on_disk>, DirstateV2ParseError> {
287 match self {
287 match self {
288 NodeRef::InMemory(_path, node) => Ok(node.children.as_ref()),
288 NodeRef::InMemory(_path, node) => Ok(node.children.as_ref()),
289 NodeRef::OnDisk(node) => {
289 NodeRef::OnDisk(node) => {
290 Ok(ChildNodesRef::OnDisk(node.children(on_disk)?))
290 Ok(ChildNodesRef::OnDisk(node.children(on_disk)?))
291 }
291 }
292 }
292 }
293 }
293 }
294
294
295 pub(super) fn has_copy_source(&self) -> bool {
295 pub(super) fn has_copy_source(&self) -> bool {
296 match self {
296 match self {
297 NodeRef::InMemory(_path, node) => node.copy_source.is_some(),
297 NodeRef::InMemory(_path, node) => node.copy_source.is_some(),
298 NodeRef::OnDisk(node) => node.has_copy_source(),
298 NodeRef::OnDisk(node) => node.has_copy_source(),
299 }
299 }
300 }
300 }
301
301
302 pub(super) fn copy_source(
302 pub(super) fn copy_source(
303 &self,
303 &self,
304 on_disk: &'on_disk [u8],
304 on_disk: &'on_disk [u8],
305 ) -> Result<Option<&'tree HgPath>, DirstateV2ParseError> {
305 ) -> Result<Option<&'tree HgPath>, DirstateV2ParseError> {
306 match self {
306 match self {
307 NodeRef::InMemory(_path, node) => {
307 NodeRef::InMemory(_path, node) => {
308 Ok(node.copy_source.as_ref().map(|s| &**s))
308 Ok(node.copy_source.as_ref().map(|s| &**s))
309 }
309 }
310 NodeRef::OnDisk(node) => node.copy_source(on_disk),
310 NodeRef::OnDisk(node) => node.copy_source(on_disk),
311 }
311 }
312 }
312 }
313 /// Returns a `BorrowedPath`, which can be turned into a `Cow<'on_disk,
313 /// Returns a `BorrowedPath`, which can be turned into a `Cow<'on_disk,
314 /// HgPath>` detached from `'tree`
314 /// HgPath>` detached from `'tree`
315 pub(super) fn copy_source_borrowed(
315 pub(super) fn copy_source_borrowed(
316 &self,
316 &self,
317 on_disk: &'on_disk [u8],
317 on_disk: &'on_disk [u8],
318 ) -> Result<Option<BorrowedPath<'tree, 'on_disk>>, DirstateV2ParseError>
318 ) -> Result<Option<BorrowedPath<'tree, 'on_disk>>, DirstateV2ParseError>
319 {
319 {
320 Ok(match self {
320 Ok(match self {
321 NodeRef::InMemory(_path, node) => {
321 NodeRef::InMemory(_path, node) => {
322 node.copy_source.as_ref().map(|source| match source {
322 node.copy_source.as_ref().map(|source| match source {
323 Cow::Borrowed(on_disk) => BorrowedPath::OnDisk(on_disk),
323 Cow::Borrowed(on_disk) => BorrowedPath::OnDisk(on_disk),
324 Cow::Owned(in_memory) => BorrowedPath::InMemory(in_memory),
324 Cow::Owned(in_memory) => BorrowedPath::InMemory(in_memory),
325 })
325 })
326 }
326 }
327 NodeRef::OnDisk(node) => node
327 NodeRef::OnDisk(node) => node
328 .copy_source(on_disk)?
328 .copy_source(on_disk)?
329 .map(|source| BorrowedPath::OnDisk(source)),
329 .map(|source| BorrowedPath::OnDisk(source)),
330 })
330 })
331 }
331 }
332
332
333 pub(super) fn entry(
333 pub(super) fn entry(
334 &self,
334 &self,
335 ) -> Result<Option<DirstateEntry>, DirstateV2ParseError> {
335 ) -> Result<Option<DirstateEntry>, DirstateV2ParseError> {
336 match self {
336 match self {
337 NodeRef::InMemory(_path, node) => {
337 NodeRef::InMemory(_path, node) => {
338 Ok(node.data.as_entry().copied())
338 Ok(node.data.as_entry().copied())
339 }
339 }
340 NodeRef::OnDisk(node) => node.entry(),
340 NodeRef::OnDisk(node) => node.entry(),
341 }
341 }
342 }
342 }
343
343
344 pub(super) fn state(
344 pub(super) fn state(
345 &self,
345 &self,
346 ) -> Result<Option<EntryState>, DirstateV2ParseError> {
346 ) -> Result<Option<EntryState>, DirstateV2ParseError> {
347 Ok(self.entry()?.and_then(|e| {
347 Ok(self.entry()?.and_then(|e| {
348 if e.any_tracked() {
348 if e.any_tracked() {
349 Some(e.state())
349 Some(e.state())
350 } else {
350 } else {
351 None
351 None
352 }
352 }
353 }))
353 }))
354 }
354 }
355
355
356 pub(super) fn cached_directory_mtime(
356 pub(super) fn cached_directory_mtime(
357 &self,
357 &self,
358 ) -> Result<Option<TruncatedTimestamp>, DirstateV2ParseError> {
358 ) -> Result<Option<TruncatedTimestamp>, DirstateV2ParseError> {
359 match self {
359 match self {
360 NodeRef::InMemory(_path, node) => Ok(match node.data {
360 NodeRef::InMemory(_path, node) => Ok(match node.data {
361 NodeData::CachedDirectory { mtime } => Some(mtime),
361 NodeData::CachedDirectory { mtime } => Some(mtime),
362 _ => None,
362 _ => None,
363 }),
363 }),
364 NodeRef::OnDisk(node) => node.cached_directory_mtime(),
364 NodeRef::OnDisk(node) => node.cached_directory_mtime(),
365 }
365 }
366 }
366 }
367
367
368 pub(super) fn descendants_with_entry_count(&self) -> u32 {
368 pub(super) fn descendants_with_entry_count(&self) -> u32 {
369 match self {
369 match self {
370 NodeRef::InMemory(_path, node) => {
370 NodeRef::InMemory(_path, node) => {
371 node.descendants_with_entry_count
371 node.descendants_with_entry_count
372 }
372 }
373 NodeRef::OnDisk(node) => node.descendants_with_entry_count.get(),
373 NodeRef::OnDisk(node) => node.descendants_with_entry_count.get(),
374 }
374 }
375 }
375 }
376
376
377 pub(super) fn tracked_descendants_count(&self) -> u32 {
377 pub(super) fn tracked_descendants_count(&self) -> u32 {
378 match self {
378 match self {
379 NodeRef::InMemory(_path, node) => node.tracked_descendants_count,
379 NodeRef::InMemory(_path, node) => node.tracked_descendants_count,
380 NodeRef::OnDisk(node) => node.tracked_descendants_count.get(),
380 NodeRef::OnDisk(node) => node.tracked_descendants_count.get(),
381 }
381 }
382 }
382 }
383 }
383 }
384
384
385 /// Represents a file or a directory
385 /// Represents a file or a directory
386 #[derive(Default)]
386 #[derive(Default)]
387 pub(super) struct Node<'on_disk> {
387 pub(super) struct Node<'on_disk> {
388 pub(super) data: NodeData,
388 pub(super) data: NodeData,
389
389
390 pub(super) copy_source: Option<Cow<'on_disk, HgPath>>,
390 pub(super) copy_source: Option<Cow<'on_disk, HgPath>>,
391
391
392 pub(super) children: ChildNodes<'on_disk>,
392 pub(super) children: ChildNodes<'on_disk>,
393
393
394 /// How many (non-inclusive) descendants of this node have an entry.
394 /// How many (non-inclusive) descendants of this node have an entry.
395 pub(super) descendants_with_entry_count: u32,
395 pub(super) descendants_with_entry_count: u32,
396
396
397 /// How many (non-inclusive) descendants of this node have an entry whose
397 /// How many (non-inclusive) descendants of this node have an entry whose
398 /// state is "tracked".
398 /// state is "tracked".
399 pub(super) tracked_descendants_count: u32,
399 pub(super) tracked_descendants_count: u32,
400 }
400 }
401
401
402 pub(super) enum NodeData {
402 pub(super) enum NodeData {
403 Entry(DirstateEntry),
403 Entry(DirstateEntry),
404 CachedDirectory { mtime: TruncatedTimestamp },
404 CachedDirectory { mtime: TruncatedTimestamp },
405 None,
405 None,
406 }
406 }
407
407
408 impl Default for NodeData {
408 impl Default for NodeData {
409 fn default() -> Self {
409 fn default() -> Self {
410 NodeData::None
410 NodeData::None
411 }
411 }
412 }
412 }
413
413
414 impl NodeData {
414 impl NodeData {
415 fn has_entry(&self) -> bool {
415 fn has_entry(&self) -> bool {
416 match self {
416 match self {
417 NodeData::Entry(_) => true,
417 NodeData::Entry(_) => true,
418 _ => false,
418 _ => false,
419 }
419 }
420 }
420 }
421
421
422 fn as_entry(&self) -> Option<&DirstateEntry> {
422 fn as_entry(&self) -> Option<&DirstateEntry> {
423 match self {
423 match self {
424 NodeData::Entry(entry) => Some(entry),
424 NodeData::Entry(entry) => Some(entry),
425 _ => None,
425 _ => None,
426 }
426 }
427 }
427 }
428
428
429 fn as_entry_mut(&mut self) -> Option<&mut DirstateEntry> {
429 fn as_entry_mut(&mut self) -> Option<&mut DirstateEntry> {
430 match self {
430 match self {
431 NodeData::Entry(entry) => Some(entry),
431 NodeData::Entry(entry) => Some(entry),
432 _ => None,
432 _ => None,
433 }
433 }
434 }
434 }
435 }
435 }
436
436
437 impl<'on_disk> DirstateMap<'on_disk> {
437 impl<'on_disk> DirstateMap<'on_disk> {
438 pub(super) fn empty(on_disk: &'on_disk [u8]) -> Self {
438 pub(super) fn empty(on_disk: &'on_disk [u8]) -> Self {
439 Self {
439 Self {
440 on_disk,
440 on_disk,
441 root: ChildNodes::default(),
441 root: ChildNodes::default(),
442 nodes_with_entry_count: 0,
442 nodes_with_entry_count: 0,
443 nodes_with_copy_source_count: 0,
443 nodes_with_copy_source_count: 0,
444 ignore_patterns_hash: [0; on_disk::IGNORE_PATTERNS_HASH_LEN],
444 ignore_patterns_hash: [0; on_disk::IGNORE_PATTERNS_HASH_LEN],
445 unreachable_bytes: 0,
445 unreachable_bytes: 0,
446 }
446 }
447 }
447 }
448
448
449 #[timed]
449 #[timed]
450 pub fn new_v2(
450 pub fn new_v2(
451 on_disk: &'on_disk [u8],
451 on_disk: &'on_disk [u8],
452 data_size: usize,
452 data_size: usize,
453 metadata: &[u8],
453 metadata: &[u8],
454 ) -> Result<Self, DirstateError> {
454 ) -> Result<Self, DirstateError> {
455 if let Some(data) = on_disk.get(..data_size) {
455 if let Some(data) = on_disk.get(..data_size) {
456 Ok(on_disk::read(data, metadata)?)
456 Ok(on_disk::read(data, metadata)?)
457 } else {
457 } else {
458 Err(DirstateV2ParseError.into())
458 Err(DirstateV2ParseError.into())
459 }
459 }
460 }
460 }
461
461
462 #[timed]
462 #[timed]
463 pub fn new_v1(
463 pub fn new_v1(
464 on_disk: &'on_disk [u8],
464 on_disk: &'on_disk [u8],
465 ) -> Result<(Self, Option<DirstateParents>), DirstateError> {
465 ) -> Result<(Self, Option<DirstateParents>), DirstateError> {
466 let mut map = Self::empty(on_disk);
466 let mut map = Self::empty(on_disk);
467 if map.on_disk.is_empty() {
467 if map.on_disk.is_empty() {
468 return Ok((map, None));
468 return Ok((map, None));
469 }
469 }
470
470
471 let parents = parse_dirstate_entries(
471 let parents = parse_dirstate_entries(
472 map.on_disk,
472 map.on_disk,
473 |path, entry, copy_source| {
473 |path, entry, copy_source| {
474 let tracked = entry.state().is_tracked();
474 let tracked = entry.state().is_tracked();
475 let node = Self::get_or_insert_node(
475 let node = Self::get_or_insert_node(
476 map.on_disk,
476 map.on_disk,
477 &mut map.unreachable_bytes,
477 &mut map.unreachable_bytes,
478 &mut map.root,
478 &mut map.root,
479 path,
479 path,
480 WithBasename::to_cow_borrowed,
480 WithBasename::to_cow_borrowed,
481 |ancestor| {
481 |ancestor| {
482 if tracked {
482 if tracked {
483 ancestor.tracked_descendants_count += 1
483 ancestor.tracked_descendants_count += 1
484 }
484 }
485 ancestor.descendants_with_entry_count += 1
485 ancestor.descendants_with_entry_count += 1
486 },
486 },
487 )?;
487 )?;
488 assert!(
488 assert!(
489 !node.data.has_entry(),
489 !node.data.has_entry(),
490 "duplicate dirstate entry in read"
490 "duplicate dirstate entry in read"
491 );
491 );
492 assert!(
492 assert!(
493 node.copy_source.is_none(),
493 node.copy_source.is_none(),
494 "duplicate dirstate entry in read"
494 "duplicate dirstate entry in read"
495 );
495 );
496 node.data = NodeData::Entry(*entry);
496 node.data = NodeData::Entry(*entry);
497 node.copy_source = copy_source.map(Cow::Borrowed);
497 node.copy_source = copy_source.map(Cow::Borrowed);
498 map.nodes_with_entry_count += 1;
498 map.nodes_with_entry_count += 1;
499 if copy_source.is_some() {
499 if copy_source.is_some() {
500 map.nodes_with_copy_source_count += 1
500 map.nodes_with_copy_source_count += 1
501 }
501 }
502 Ok(())
502 Ok(())
503 },
503 },
504 )?;
504 )?;
505 let parents = Some(parents.clone());
505 let parents = Some(parents.clone());
506
506
507 Ok((map, parents))
507 Ok((map, parents))
508 }
508 }
509
509
510 /// Assuming dirstate-v2 format, returns whether the next write should
510 /// Assuming dirstate-v2 format, returns whether the next write should
511 /// append to the existing data file that contains `self.on_disk` (true),
511 /// append to the existing data file that contains `self.on_disk` (true),
512 /// or create a new data file from scratch (false).
512 /// or create a new data file from scratch (false).
513 pub(super) fn write_should_append(&self) -> bool {
513 pub(super) fn write_should_append(&self) -> bool {
514 let ratio = self.unreachable_bytes as f32 / self.on_disk.len() as f32;
514 let ratio = self.unreachable_bytes as f32 / self.on_disk.len() as f32;
515 ratio < ACCEPTABLE_UNREACHABLE_BYTES_RATIO
515 ratio < ACCEPTABLE_UNREACHABLE_BYTES_RATIO
516 }
516 }
517
517
518 fn get_node<'tree>(
518 fn get_node<'tree>(
519 &'tree self,
519 &'tree self,
520 path: &HgPath,
520 path: &HgPath,
521 ) -> Result<Option<NodeRef<'tree, 'on_disk>>, DirstateV2ParseError> {
521 ) -> Result<Option<NodeRef<'tree, 'on_disk>>, DirstateV2ParseError> {
522 let mut children = self.root.as_ref();
522 let mut children = self.root.as_ref();
523 let mut components = path.components();
523 let mut components = path.components();
524 let mut component =
524 let mut component =
525 components.next().expect("expected at least one components");
525 components.next().expect("expected at least one components");
526 loop {
526 loop {
527 if let Some(child) = children.get(component, self.on_disk)? {
527 if let Some(child) = children.get(component, self.on_disk)? {
528 if let Some(next_component) = components.next() {
528 if let Some(next_component) = components.next() {
529 component = next_component;
529 component = next_component;
530 children = child.children(self.on_disk)?;
530 children = child.children(self.on_disk)?;
531 } else {
531 } else {
532 return Ok(Some(child));
532 return Ok(Some(child));
533 }
533 }
534 } else {
534 } else {
535 return Ok(None);
535 return Ok(None);
536 }
536 }
537 }
537 }
538 }
538 }
539
539
540 /// Returns a mutable reference to the node at `path` if it exists
540 /// Returns a mutable reference to the node at `path` if it exists
541 ///
541 ///
542 /// This takes `root` instead of `&mut self` so that callers can mutate
542 /// This takes `root` instead of `&mut self` so that callers can mutate
543 /// other fields while the returned borrow is still valid
543 /// other fields while the returned borrow is still valid
544 fn get_node_mut<'tree>(
544 fn get_node_mut<'tree>(
545 on_disk: &'on_disk [u8],
545 on_disk: &'on_disk [u8],
546 unreachable_bytes: &mut u32,
546 unreachable_bytes: &mut u32,
547 root: &'tree mut ChildNodes<'on_disk>,
547 root: &'tree mut ChildNodes<'on_disk>,
548 path: &HgPath,
548 path: &HgPath,
549 ) -> Result<Option<&'tree mut Node<'on_disk>>, DirstateV2ParseError> {
549 ) -> Result<Option<&'tree mut Node<'on_disk>>, DirstateV2ParseError> {
550 let mut children = root;
550 let mut children = root;
551 let mut components = path.components();
551 let mut components = path.components();
552 let mut component =
552 let mut component =
553 components.next().expect("expected at least one components");
553 components.next().expect("expected at least one components");
554 loop {
554 loop {
555 if let Some(child) = children
555 if let Some(child) = children
556 .make_mut(on_disk, unreachable_bytes)?
556 .make_mut(on_disk, unreachable_bytes)?
557 .get_mut(component)
557 .get_mut(component)
558 {
558 {
559 if let Some(next_component) = components.next() {
559 if let Some(next_component) = components.next() {
560 component = next_component;
560 component = next_component;
561 children = &mut child.children;
561 children = &mut child.children;
562 } else {
562 } else {
563 return Ok(Some(child));
563 return Ok(Some(child));
564 }
564 }
565 } else {
565 } else {
566 return Ok(None);
566 return Ok(None);
567 }
567 }
568 }
568 }
569 }
569 }
570
570
571 pub(super) fn get_or_insert<'tree, 'path>(
571 pub(super) fn get_or_insert<'tree, 'path>(
572 &'tree mut self,
572 &'tree mut self,
573 path: &HgPath,
573 path: &HgPath,
574 ) -> Result<&'tree mut Node<'on_disk>, DirstateV2ParseError> {
574 ) -> Result<&'tree mut Node<'on_disk>, DirstateV2ParseError> {
575 Self::get_or_insert_node(
575 Self::get_or_insert_node(
576 self.on_disk,
576 self.on_disk,
577 &mut self.unreachable_bytes,
577 &mut self.unreachable_bytes,
578 &mut self.root,
578 &mut self.root,
579 path,
579 path,
580 WithBasename::to_cow_owned,
580 WithBasename::to_cow_owned,
581 |_| {},
581 |_| {},
582 )
582 )
583 }
583 }
584
584
585 fn get_or_insert_node<'tree, 'path>(
585 fn get_or_insert_node<'tree, 'path>(
586 on_disk: &'on_disk [u8],
586 on_disk: &'on_disk [u8],
587 unreachable_bytes: &mut u32,
587 unreachable_bytes: &mut u32,
588 root: &'tree mut ChildNodes<'on_disk>,
588 root: &'tree mut ChildNodes<'on_disk>,
589 path: &'path HgPath,
589 path: &'path HgPath,
590 to_cow: impl Fn(
590 to_cow: impl Fn(
591 WithBasename<&'path HgPath>,
591 WithBasename<&'path HgPath>,
592 ) -> WithBasename<Cow<'on_disk, HgPath>>,
592 ) -> WithBasename<Cow<'on_disk, HgPath>>,
593 mut each_ancestor: impl FnMut(&mut Node),
593 mut each_ancestor: impl FnMut(&mut Node),
594 ) -> Result<&'tree mut Node<'on_disk>, DirstateV2ParseError> {
594 ) -> Result<&'tree mut Node<'on_disk>, DirstateV2ParseError> {
595 let mut child_nodes = root;
595 let mut child_nodes = root;
596 let mut inclusive_ancestor_paths =
596 let mut inclusive_ancestor_paths =
597 WithBasename::inclusive_ancestors_of(path);
597 WithBasename::inclusive_ancestors_of(path);
598 let mut ancestor_path = inclusive_ancestor_paths
598 let mut ancestor_path = inclusive_ancestor_paths
599 .next()
599 .next()
600 .expect("expected at least one inclusive ancestor");
600 .expect("expected at least one inclusive ancestor");
601 loop {
601 loop {
602 let (_, child_node) = child_nodes
602 let (_, child_node) = child_nodes
603 .make_mut(on_disk, unreachable_bytes)?
603 .make_mut(on_disk, unreachable_bytes)?
604 .raw_entry_mut()
604 .raw_entry_mut()
605 .from_key(ancestor_path.base_name())
605 .from_key(ancestor_path.base_name())
606 .or_insert_with(|| (to_cow(ancestor_path), Node::default()));
606 .or_insert_with(|| (to_cow(ancestor_path), Node::default()));
607 if let Some(next) = inclusive_ancestor_paths.next() {
607 if let Some(next) = inclusive_ancestor_paths.next() {
608 each_ancestor(child_node);
608 each_ancestor(child_node);
609 ancestor_path = next;
609 ancestor_path = next;
610 child_nodes = &mut child_node.children;
610 child_nodes = &mut child_node.children;
611 } else {
611 } else {
612 return Ok(child_node);
612 return Ok(child_node);
613 }
613 }
614 }
614 }
615 }
615 }
616
616
617 fn reset_state(
617 fn reset_state(
618 &mut self,
618 &mut self,
619 filename: &HgPath,
619 filename: &HgPath,
620 old_entry_opt: Option<DirstateEntry>,
620 old_entry_opt: Option<DirstateEntry>,
621 wc_tracked: bool,
621 wc_tracked: bool,
622 p1_tracked: bool,
622 p1_tracked: bool,
623 p2_info: bool,
623 p2_info: bool,
624 has_meaningful_mtime: bool,
624 has_meaningful_mtime: bool,
625 parent_file_data_opt: Option<ParentFileData>,
625 parent_file_data_opt: Option<ParentFileData>,
626 ) -> Result<(), DirstateError> {
626 ) -> Result<(), DirstateError> {
627 let (had_entry, was_tracked) = match old_entry_opt {
627 let (had_entry, was_tracked) = match old_entry_opt {
628 Some(old_entry) => (true, old_entry.tracked()),
628 Some(old_entry) => (true, old_entry.tracked()),
629 None => (false, false),
629 None => (false, false),
630 };
630 };
631 let node = Self::get_or_insert_node(
631 let node = Self::get_or_insert_node(
632 self.on_disk,
632 self.on_disk,
633 &mut self.unreachable_bytes,
633 &mut self.unreachable_bytes,
634 &mut self.root,
634 &mut self.root,
635 filename,
635 filename,
636 WithBasename::to_cow_owned,
636 WithBasename::to_cow_owned,
637 |ancestor| {
637 |ancestor| {
638 if !had_entry {
638 if !had_entry {
639 ancestor.descendants_with_entry_count += 1;
639 ancestor.descendants_with_entry_count += 1;
640 }
640 }
641 if was_tracked {
641 if was_tracked {
642 if !wc_tracked {
642 if !wc_tracked {
643 ancestor.tracked_descendants_count = ancestor
643 ancestor.tracked_descendants_count = ancestor
644 .tracked_descendants_count
644 .tracked_descendants_count
645 .checked_sub(1)
645 .checked_sub(1)
646 .expect("tracked count to be >= 0");
646 .expect("tracked count to be >= 0");
647 }
647 }
648 } else {
648 } else {
649 if wc_tracked {
649 if wc_tracked {
650 ancestor.tracked_descendants_count += 1;
650 ancestor.tracked_descendants_count += 1;
651 }
651 }
652 }
652 }
653 },
653 },
654 )?;
654 )?;
655
655
656 let v2_data = if let Some(parent_file_data) = parent_file_data_opt {
656 let v2_data = if let Some(parent_file_data) = parent_file_data_opt {
657 DirstateV2Data {
657 DirstateV2Data {
658 wc_tracked,
658 wc_tracked,
659 p1_tracked,
659 p1_tracked,
660 p2_info,
660 p2_info,
661 mode_size: parent_file_data.mode_size,
661 mode_size: parent_file_data.mode_size,
662 mtime: if has_meaningful_mtime {
662 mtime: if has_meaningful_mtime {
663 parent_file_data.mtime
663 parent_file_data.mtime
664 } else {
664 } else {
665 None
665 None
666 },
666 },
667 ..Default::default()
667 ..Default::default()
668 }
668 }
669 } else {
669 } else {
670 DirstateV2Data {
670 DirstateV2Data {
671 wc_tracked,
671 wc_tracked,
672 p1_tracked,
672 p1_tracked,
673 p2_info,
673 p2_info,
674 ..Default::default()
674 ..Default::default()
675 }
675 }
676 };
676 };
677 if !had_entry {
677 if !had_entry {
678 self.nodes_with_entry_count += 1;
678 self.nodes_with_entry_count += 1;
679 }
679 }
680 node.data = NodeData::Entry(DirstateEntry::from_v2_data(v2_data));
680 node.data = NodeData::Entry(DirstateEntry::from_v2_data(v2_data));
681 Ok(())
681 Ok(())
682 }
682 }
683
683
684 fn set_tracked(
684 fn set_tracked(
685 &mut self,
685 &mut self,
686 filename: &HgPath,
686 filename: &HgPath,
687 old_entry_opt: Option<DirstateEntry>,
687 old_entry_opt: Option<DirstateEntry>,
688 ) -> Result<bool, DirstateV2ParseError> {
688 ) -> Result<bool, DirstateV2ParseError> {
689 let was_tracked = old_entry_opt.map_or(false, |e| e.tracked());
689 let was_tracked = old_entry_opt.map_or(false, |e| e.tracked());
690 let had_entry = old_entry_opt.is_some();
690 let had_entry = old_entry_opt.is_some();
691 let tracked_count_increment = if was_tracked { 0 } else { 1 };
691 let tracked_count_increment = if was_tracked { 0 } else { 1 };
692 let mut new = false;
692 let mut new = false;
693
693
694 let node = Self::get_or_insert_node(
694 let node = Self::get_or_insert_node(
695 self.on_disk,
695 self.on_disk,
696 &mut self.unreachable_bytes,
696 &mut self.unreachable_bytes,
697 &mut self.root,
697 &mut self.root,
698 filename,
698 filename,
699 WithBasename::to_cow_owned,
699 WithBasename::to_cow_owned,
700 |ancestor| {
700 |ancestor| {
701 if !had_entry {
701 if !had_entry {
702 ancestor.descendants_with_entry_count += 1;
702 ancestor.descendants_with_entry_count += 1;
703 }
703 }
704
704
705 ancestor.tracked_descendants_count += tracked_count_increment;
705 ancestor.tracked_descendants_count += tracked_count_increment;
706 },
706 },
707 )?;
707 )?;
708 let new_entry = if let Some(old_entry) = old_entry_opt {
708 let new_entry = if let Some(old_entry) = old_entry_opt {
709 let mut e = old_entry.clone();
709 let mut e = old_entry.clone();
710 if e.tracked() {
710 if e.tracked() {
711 // XXX
711 // XXX
712 // This is probably overkill for more case, but we need this to
712 // This is probably overkill for more case, but we need this to
713 // fully replace the `normallookup` call with `set_tracked`
713 // fully replace the `normallookup` call with `set_tracked`
714 // one. Consider smoothing this in the future.
714 // one. Consider smoothing this in the future.
715 e.set_possibly_dirty();
715 e.set_possibly_dirty();
716 } else {
716 } else {
717 new = true;
717 new = true;
718 e.set_tracked();
718 e.set_tracked();
719 }
719 }
720 e
720 e
721 } else {
721 } else {
722 self.nodes_with_entry_count += 1;
722 self.nodes_with_entry_count += 1;
723 new = true;
723 new = true;
724 DirstateEntry::new_tracked()
724 DirstateEntry::new_tracked()
725 };
725 };
726 node.data = NodeData::Entry(new_entry);
726 node.data = NodeData::Entry(new_entry);
727 Ok(new)
727 Ok(new)
728 }
728 }
729
729
730 /// It is the responsibility of the caller to know that there was an entry
730 /// It is the responsibility of the caller to know that there was an entry
731 /// there before. Does not handle the removal of copy source
731 /// there before. Does not handle the removal of copy source
732 fn set_untracked(
732 fn set_untracked(
733 &mut self,
733 &mut self,
734 filename: &HgPath,
734 filename: &HgPath,
735 old_entry: DirstateEntry,
735 old_entry: DirstateEntry,
736 ) -> Result<(), DirstateV2ParseError> {
736 ) -> Result<(), DirstateV2ParseError> {
737 let node = Self::get_or_insert_node(
737 let node = Self::get_or_insert_node(
738 self.on_disk,
738 self.on_disk,
739 &mut self.unreachable_bytes,
739 &mut self.unreachable_bytes,
740 &mut self.root,
740 &mut self.root,
741 filename,
741 filename,
742 WithBasename::to_cow_owned,
742 WithBasename::to_cow_owned,
743 |ancestor| {
743 |ancestor| {
744 ancestor.tracked_descendants_count = ancestor
744 ancestor.tracked_descendants_count = ancestor
745 .tracked_descendants_count
745 .tracked_descendants_count
746 .checked_sub(1)
746 .checked_sub(1)
747 .expect("tracked_descendants_count should be >= 0");
747 .expect("tracked_descendants_count should be >= 0");
748 },
748 },
749 )?;
749 )?;
750 let mut new_entry = old_entry.clone();
750 let mut new_entry = old_entry.clone();
751 new_entry.set_untracked();
751 new_entry.set_untracked();
752 node.data = NodeData::Entry(new_entry);
752 node.data = NodeData::Entry(new_entry);
753 Ok(())
753 Ok(())
754 }
754 }
755
755
756 fn set_clean(
756 fn set_clean(
757 &mut self,
757 &mut self,
758 filename: &HgPath,
758 filename: &HgPath,
759 old_entry: DirstateEntry,
759 old_entry: DirstateEntry,
760 mode: u32,
760 mode: u32,
761 size: u32,
761 size: u32,
762 mtime: TruncatedTimestamp,
762 mtime: TruncatedTimestamp,
763 ) -> Result<(), DirstateError> {
763 ) -> Result<(), DirstateError> {
764 let node = Self::get_or_insert_node(
764 let node = Self::get_or_insert_node(
765 self.on_disk,
765 self.on_disk,
766 &mut self.unreachable_bytes,
766 &mut self.unreachable_bytes,
767 &mut self.root,
767 &mut self.root,
768 filename,
768 filename,
769 WithBasename::to_cow_owned,
769 WithBasename::to_cow_owned,
770 |ancestor| {
770 |ancestor| {
771 if !old_entry.tracked() {
771 if !old_entry.tracked() {
772 ancestor.tracked_descendants_count += 1;
772 ancestor.tracked_descendants_count += 1;
773 }
773 }
774 },
774 },
775 )?;
775 )?;
776 let mut new_entry = old_entry.clone();
776 let mut new_entry = old_entry.clone();
777 new_entry.set_clean(mode, size, mtime);
777 new_entry.set_clean(mode, size, mtime);
778 node.data = NodeData::Entry(new_entry);
778 node.data = NodeData::Entry(new_entry);
779 Ok(())
779 Ok(())
780 }
780 }
781
781
782 fn set_possibly_dirty(
782 fn set_possibly_dirty(
783 &mut self,
783 &mut self,
784 filename: &HgPath,
784 filename: &HgPath,
785 ) -> Result<(), DirstateError> {
785 ) -> Result<(), DirstateError> {
786 let node = Self::get_or_insert_node(
786 let node = Self::get_or_insert_node(
787 self.on_disk,
787 self.on_disk,
788 &mut self.unreachable_bytes,
788 &mut self.unreachable_bytes,
789 &mut self.root,
789 &mut self.root,
790 filename,
790 filename,
791 WithBasename::to_cow_owned,
791 WithBasename::to_cow_owned,
792 |_ancestor| {},
792 |_ancestor| {},
793 )?;
793 )?;
794 let entry = node.data.as_entry_mut().expect("entry should exist");
794 let entry = node.data.as_entry_mut().expect("entry should exist");
795 entry.set_possibly_dirty();
795 entry.set_possibly_dirty();
796 node.data = NodeData::Entry(*entry);
796 node.data = NodeData::Entry(*entry);
797 Ok(())
797 Ok(())
798 }
798 }
799
799
800 fn iter_nodes<'tree>(
800 fn iter_nodes<'tree>(
801 &'tree self,
801 &'tree self,
802 ) -> impl Iterator<
802 ) -> impl Iterator<
803 Item = Result<NodeRef<'tree, 'on_disk>, DirstateV2ParseError>,
803 Item = Result<NodeRef<'tree, 'on_disk>, DirstateV2ParseError>,
804 > + 'tree {
804 > + 'tree {
805 // Depth first tree traversal.
805 // Depth first tree traversal.
806 //
806 //
807 // If we could afford internal iteration and recursion,
807 // If we could afford internal iteration and recursion,
808 // this would look like:
808 // this would look like:
809 //
809 //
810 // ```
810 // ```
811 // fn traverse_children(
811 // fn traverse_children(
812 // children: &ChildNodes,
812 // children: &ChildNodes,
813 // each: &mut impl FnMut(&Node),
813 // each: &mut impl FnMut(&Node),
814 // ) {
814 // ) {
815 // for child in children.values() {
815 // for child in children.values() {
816 // traverse_children(&child.children, each);
816 // traverse_children(&child.children, each);
817 // each(child);
817 // each(child);
818 // }
818 // }
819 // }
819 // }
820 // ```
820 // ```
821 //
821 //
822 // However we want an external iterator and therefore can’t use the
822 // However we want an external iterator and therefore can’t use the
823 // call stack. Use an explicit stack instead:
823 // call stack. Use an explicit stack instead:
824 let mut stack = Vec::new();
824 let mut stack = Vec::new();
825 let mut iter = self.root.as_ref().iter();
825 let mut iter = self.root.as_ref().iter();
826 std::iter::from_fn(move || {
826 std::iter::from_fn(move || {
827 while let Some(child_node) = iter.next() {
827 while let Some(child_node) = iter.next() {
828 let children = match child_node.children(self.on_disk) {
828 let children = match child_node.children(self.on_disk) {
829 Ok(children) => children,
829 Ok(children) => children,
830 Err(error) => return Some(Err(error)),
830 Err(error) => return Some(Err(error)),
831 };
831 };
832 // Pseudo-recursion
832 // Pseudo-recursion
833 let new_iter = children.iter();
833 let new_iter = children.iter();
834 let old_iter = std::mem::replace(&mut iter, new_iter);
834 let old_iter = std::mem::replace(&mut iter, new_iter);
835 stack.push((child_node, old_iter));
835 stack.push((child_node, old_iter));
836 }
836 }
837 // Found the end of a `children.iter()` iterator.
837 // Found the end of a `children.iter()` iterator.
838 if let Some((child_node, next_iter)) = stack.pop() {
838 if let Some((child_node, next_iter)) = stack.pop() {
839 // "Return" from pseudo-recursion by restoring state from the
839 // "Return" from pseudo-recursion by restoring state from the
840 // explicit stack
840 // explicit stack
841 iter = next_iter;
841 iter = next_iter;
842
842
843 Some(Ok(child_node))
843 Some(Ok(child_node))
844 } else {
844 } else {
845 // Reached the bottom of the stack, we’re done
845 // Reached the bottom of the stack, we’re done
846 None
846 None
847 }
847 }
848 })
848 })
849 }
849 }
850
850
851 fn count_dropped_path(unreachable_bytes: &mut u32, path: &Cow<HgPath>) {
851 fn count_dropped_path(unreachable_bytes: &mut u32, path: &Cow<HgPath>) {
852 if let Cow::Borrowed(path) = path {
852 if let Cow::Borrowed(path) = path {
853 *unreachable_bytes += path.len() as u32
853 *unreachable_bytes += path.len() as u32
854 }
854 }
855 }
855 }
856 }
856 }
857
857
858 /// Like `Iterator::filter_map`, but over a fallible iterator of `Result`s.
858 /// Like `Iterator::filter_map`, but over a fallible iterator of `Result`s.
859 ///
859 ///
860 /// The callback is only called for incoming `Ok` values. Errors are passed
860 /// The callback is only called for incoming `Ok` values. Errors are passed
861 /// through as-is. In order to let it use the `?` operator the callback is
861 /// through as-is. In order to let it use the `?` operator the callback is
862 /// expected to return a `Result` of `Option`, instead of an `Option` of
862 /// expected to return a `Result` of `Option`, instead of an `Option` of
863 /// `Result`.
863 /// `Result`.
864 fn filter_map_results<'a, I, F, A, B, E>(
864 fn filter_map_results<'a, I, F, A, B, E>(
865 iter: I,
865 iter: I,
866 f: F,
866 f: F,
867 ) -> impl Iterator<Item = Result<B, E>> + 'a
867 ) -> impl Iterator<Item = Result<B, E>> + 'a
868 where
868 where
869 I: Iterator<Item = Result<A, E>> + 'a,
869 I: Iterator<Item = Result<A, E>> + 'a,
870 F: Fn(A) -> Result<Option<B>, E> + 'a,
870 F: Fn(A) -> Result<Option<B>, E> + 'a,
871 {
871 {
872 iter.filter_map(move |result| match result {
872 iter.filter_map(move |result| match result {
873 Ok(node) => f(node).transpose(),
873 Ok(node) => f(node).transpose(),
874 Err(e) => Some(Err(e)),
874 Err(e) => Some(Err(e)),
875 })
875 })
876 }
876 }
877
877
878 impl OwningDirstateMap {
878 impl OwningDirstateMap {
879 pub fn clear(&mut self) {
879 pub fn clear(&mut self) {
880 self.with_dmap_mut(|map| {
880 self.with_dmap_mut(|map| {
881 map.root = Default::default();
881 map.root = Default::default();
882 map.nodes_with_entry_count = 0;
882 map.nodes_with_entry_count = 0;
883 map.nodes_with_copy_source_count = 0;
883 map.nodes_with_copy_source_count = 0;
884 });
884 });
885 }
885 }
886
886
887 pub fn set_tracked(
887 pub fn set_tracked(
888 &mut self,
888 &mut self,
889 filename: &HgPath,
889 filename: &HgPath,
890 ) -> Result<bool, DirstateV2ParseError> {
890 ) -> Result<bool, DirstateV2ParseError> {
891 let old_entry_opt = self.get(filename)?;
891 let old_entry_opt = self.get(filename)?;
892 self.with_dmap_mut(|map| map.set_tracked(filename, old_entry_opt))
892 self.with_dmap_mut(|map| map.set_tracked(filename, old_entry_opt))
893 }
893 }
894
894
895 pub fn set_untracked(
895 pub fn set_untracked(
896 &mut self,
896 &mut self,
897 filename: &HgPath,
897 filename: &HgPath,
898 ) -> Result<bool, DirstateError> {
898 ) -> Result<bool, DirstateError> {
899 let old_entry_opt = self.get(filename)?;
899 let old_entry_opt = self.get(filename)?;
900 match old_entry_opt {
900 match old_entry_opt {
901 None => Ok(false),
901 None => Ok(false),
902 Some(old_entry) => {
902 Some(old_entry) => {
903 if !old_entry.tracked() {
903 if !old_entry.tracked() {
904 // `DirstateMap::set_untracked` is not a noop if
904 // `DirstateMap::set_untracked` is not a noop if
905 // already not tracked as it will decrement the
905 // already not tracked as it will decrement the
906 // tracked counters while going down.
906 // tracked counters while going down.
907 return Ok(true);
907 return Ok(true);
908 }
908 }
909 if old_entry.added() {
909 if old_entry.added() {
910 // Untracking an "added" entry will just result in a
910 // Untracking an "added" entry will just result in a
911 // worthless entry (and other parts of the code will
911 // worthless entry (and other parts of the code will
912 // complain about it), just drop it entirely.
912 // complain about it), just drop it entirely.
913 self.drop_entry_and_copy_source(filename)?;
913 self.drop_entry_and_copy_source(filename)?;
914 return Ok(true);
914 return Ok(true);
915 }
915 }
916 if !old_entry.p2_info() {
916 if !old_entry.p2_info() {
917 self.copy_map_remove(filename)?;
917 self.copy_map_remove(filename)?;
918 }
918 }
919
919
920 self.with_dmap_mut(|map| {
920 self.with_dmap_mut(|map| {
921 map.set_untracked(filename, old_entry)?;
921 map.set_untracked(filename, old_entry)?;
922 Ok(true)
922 Ok(true)
923 })
923 })
924 }
924 }
925 }
925 }
926 }
926 }
927
927
928 pub fn set_clean(
928 pub fn set_clean(
929 &mut self,
929 &mut self,
930 filename: &HgPath,
930 filename: &HgPath,
931 mode: u32,
931 mode: u32,
932 size: u32,
932 size: u32,
933 mtime: TruncatedTimestamp,
933 mtime: TruncatedTimestamp,
934 ) -> Result<(), DirstateError> {
934 ) -> Result<(), DirstateError> {
935 let old_entry = match self.get(filename)? {
935 let old_entry = match self.get(filename)? {
936 None => {
936 None => {
937 return Err(
937 return Err(
938 DirstateMapError::PathNotFound(filename.into()).into()
938 DirstateMapError::PathNotFound(filename.into()).into()
939 )
939 )
940 }
940 }
941 Some(e) => e,
941 Some(e) => e,
942 };
942 };
943 self.copy_map_remove(filename)?;
943 self.copy_map_remove(filename)?;
944 self.with_dmap_mut(|map| {
944 self.with_dmap_mut(|map| {
945 map.set_clean(filename, old_entry, mode, size, mtime)
945 map.set_clean(filename, old_entry, mode, size, mtime)
946 })
946 })
947 }
947 }
948
948
949 pub fn set_possibly_dirty(
949 pub fn set_possibly_dirty(
950 &mut self,
950 &mut self,
951 filename: &HgPath,
951 filename: &HgPath,
952 ) -> Result<(), DirstateError> {
952 ) -> Result<(), DirstateError> {
953 if self.get(filename)?.is_none() {
953 if self.get(filename)?.is_none() {
954 return Err(DirstateMapError::PathNotFound(filename.into()).into());
954 return Err(DirstateMapError::PathNotFound(filename.into()).into());
955 }
955 }
956 self.with_dmap_mut(|map| map.set_possibly_dirty(filename))
956 self.with_dmap_mut(|map| map.set_possibly_dirty(filename))
957 }
957 }
958
958
959 pub fn reset_state(
959 pub fn reset_state(
960 &mut self,
960 &mut self,
961 filename: &HgPath,
961 filename: &HgPath,
962 wc_tracked: bool,
962 wc_tracked: bool,
963 p1_tracked: bool,
963 p1_tracked: bool,
964 p2_info: bool,
964 p2_info: bool,
965 has_meaningful_mtime: bool,
965 has_meaningful_mtime: bool,
966 parent_file_data_opt: Option<ParentFileData>,
966 parent_file_data_opt: Option<ParentFileData>,
967 ) -> Result<(), DirstateError> {
967 ) -> Result<(), DirstateError> {
968 if !(p1_tracked || p2_info || wc_tracked) {
968 if !(p1_tracked || p2_info || wc_tracked) {
969 self.drop_entry_and_copy_source(filename)?;
969 self.drop_entry_and_copy_source(filename)?;
970 return Ok(());
970 return Ok(());
971 }
971 }
972 self.copy_map_remove(filename)?;
972 self.copy_map_remove(filename)?;
973 let old_entry_opt = self.get(filename)?;
973 let old_entry_opt = self.get(filename)?;
974 self.with_dmap_mut(|map| {
974 self.with_dmap_mut(|map| {
975 map.reset_state(
975 map.reset_state(
976 filename,
976 filename,
977 old_entry_opt,
977 old_entry_opt,
978 wc_tracked,
978 wc_tracked,
979 p1_tracked,
979 p1_tracked,
980 p2_info,
980 p2_info,
981 has_meaningful_mtime,
981 has_meaningful_mtime,
982 parent_file_data_opt,
982 parent_file_data_opt,
983 )
983 )
984 })
984 })
985 }
985 }
986
986
987 pub fn drop_entry_and_copy_source(
987 pub fn drop_entry_and_copy_source(
988 &mut self,
988 &mut self,
989 filename: &HgPath,
989 filename: &HgPath,
990 ) -> Result<(), DirstateError> {
990 ) -> Result<(), DirstateError> {
991 let was_tracked = self
991 let was_tracked = self.get(filename)?.map_or(false, |e| e.tracked());
992 .get(filename)?
993 .map_or(false, |e| e.state().is_tracked());
994 struct Dropped {
992 struct Dropped {
995 was_tracked: bool,
993 was_tracked: bool,
996 had_entry: bool,
994 had_entry: bool,
997 had_copy_source: bool,
995 had_copy_source: bool,
998 }
996 }
999
997
1000 /// If this returns `Ok(Some((dropped, removed)))`, then
998 /// If this returns `Ok(Some((dropped, removed)))`, then
1001 ///
999 ///
1002 /// * `dropped` is about the leaf node that was at `filename`
1000 /// * `dropped` is about the leaf node that was at `filename`
1003 /// * `removed` is whether this particular level of recursion just
1001 /// * `removed` is whether this particular level of recursion just
1004 /// removed a node in `nodes`.
1002 /// removed a node in `nodes`.
1005 fn recur<'on_disk>(
1003 fn recur<'on_disk>(
1006 on_disk: &'on_disk [u8],
1004 on_disk: &'on_disk [u8],
1007 unreachable_bytes: &mut u32,
1005 unreachable_bytes: &mut u32,
1008 nodes: &mut ChildNodes<'on_disk>,
1006 nodes: &mut ChildNodes<'on_disk>,
1009 path: &HgPath,
1007 path: &HgPath,
1010 ) -> Result<Option<(Dropped, bool)>, DirstateV2ParseError> {
1008 ) -> Result<Option<(Dropped, bool)>, DirstateV2ParseError> {
1011 let (first_path_component, rest_of_path) =
1009 let (first_path_component, rest_of_path) =
1012 path.split_first_component();
1010 path.split_first_component();
1013 let nodes = nodes.make_mut(on_disk, unreachable_bytes)?;
1011 let nodes = nodes.make_mut(on_disk, unreachable_bytes)?;
1014 let node = if let Some(node) = nodes.get_mut(first_path_component)
1012 let node = if let Some(node) = nodes.get_mut(first_path_component)
1015 {
1013 {
1016 node
1014 node
1017 } else {
1015 } else {
1018 return Ok(None);
1016 return Ok(None);
1019 };
1017 };
1020 let dropped;
1018 let dropped;
1021 if let Some(rest) = rest_of_path {
1019 if let Some(rest) = rest_of_path {
1022 if let Some((d, removed)) = recur(
1020 if let Some((d, removed)) = recur(
1023 on_disk,
1021 on_disk,
1024 unreachable_bytes,
1022 unreachable_bytes,
1025 &mut node.children,
1023 &mut node.children,
1026 rest,
1024 rest,
1027 )? {
1025 )? {
1028 dropped = d;
1026 dropped = d;
1029 if dropped.had_entry {
1027 if dropped.had_entry {
1030 node.descendants_with_entry_count = node
1028 node.descendants_with_entry_count = node
1031 .descendants_with_entry_count
1029 .descendants_with_entry_count
1032 .checked_sub(1)
1030 .checked_sub(1)
1033 .expect(
1031 .expect(
1034 "descendants_with_entry_count should be >= 0",
1032 "descendants_with_entry_count should be >= 0",
1035 );
1033 );
1036 }
1034 }
1037 if dropped.was_tracked {
1035 if dropped.was_tracked {
1038 node.tracked_descendants_count = node
1036 node.tracked_descendants_count = node
1039 .tracked_descendants_count
1037 .tracked_descendants_count
1040 .checked_sub(1)
1038 .checked_sub(1)
1041 .expect(
1039 .expect(
1042 "tracked_descendants_count should be >= 0",
1040 "tracked_descendants_count should be >= 0",
1043 );
1041 );
1044 }
1042 }
1045
1043
1046 // Directory caches must be invalidated when removing a
1044 // Directory caches must be invalidated when removing a
1047 // child node
1045 // child node
1048 if removed {
1046 if removed {
1049 if let NodeData::CachedDirectory { .. } = &node.data {
1047 if let NodeData::CachedDirectory { .. } = &node.data {
1050 node.data = NodeData::None
1048 node.data = NodeData::None
1051 }
1049 }
1052 }
1050 }
1053 } else {
1051 } else {
1054 return Ok(None);
1052 return Ok(None);
1055 }
1053 }
1056 } else {
1054 } else {
1057 let entry = node.data.as_entry();
1055 let entry = node.data.as_entry();
1058 let was_tracked = entry.map_or(false, |entry| entry.tracked());
1056 let was_tracked = entry.map_or(false, |entry| entry.tracked());
1059 let had_entry = entry.is_some();
1057 let had_entry = entry.is_some();
1060 if had_entry {
1058 if had_entry {
1061 node.data = NodeData::None
1059 node.data = NodeData::None
1062 }
1060 }
1063 let mut had_copy_source = false;
1061 let mut had_copy_source = false;
1064 if let Some(source) = &node.copy_source {
1062 if let Some(source) = &node.copy_source {
1065 DirstateMap::count_dropped_path(unreachable_bytes, source);
1063 DirstateMap::count_dropped_path(unreachable_bytes, source);
1066 had_copy_source = true;
1064 had_copy_source = true;
1067 node.copy_source = None
1065 node.copy_source = None
1068 }
1066 }
1069 dropped = Dropped {
1067 dropped = Dropped {
1070 was_tracked,
1068 was_tracked,
1071 had_entry,
1069 had_entry,
1072 had_copy_source,
1070 had_copy_source,
1073 };
1071 };
1074 }
1072 }
1075 // After recursion, for both leaf (rest_of_path is None) nodes and
1073 // After recursion, for both leaf (rest_of_path is None) nodes and
1076 // parent nodes, remove a node if it just became empty.
1074 // parent nodes, remove a node if it just became empty.
1077 let remove = !node.data.has_entry()
1075 let remove = !node.data.has_entry()
1078 && node.copy_source.is_none()
1076 && node.copy_source.is_none()
1079 && node.children.is_empty();
1077 && node.children.is_empty();
1080 if remove {
1078 if remove {
1081 let (key, _) =
1079 let (key, _) =
1082 nodes.remove_entry(first_path_component).unwrap();
1080 nodes.remove_entry(first_path_component).unwrap();
1083 DirstateMap::count_dropped_path(
1081 DirstateMap::count_dropped_path(
1084 unreachable_bytes,
1082 unreachable_bytes,
1085 key.full_path(),
1083 key.full_path(),
1086 )
1084 )
1087 }
1085 }
1088 Ok(Some((dropped, remove)))
1086 Ok(Some((dropped, remove)))
1089 }
1087 }
1090
1088
1091 self.with_dmap_mut(|map| {
1089 self.with_dmap_mut(|map| {
1092 if let Some((dropped, _removed)) = recur(
1090 if let Some((dropped, _removed)) = recur(
1093 map.on_disk,
1091 map.on_disk,
1094 &mut map.unreachable_bytes,
1092 &mut map.unreachable_bytes,
1095 &mut map.root,
1093 &mut map.root,
1096 filename,
1094 filename,
1097 )? {
1095 )? {
1098 if dropped.had_entry {
1096 if dropped.had_entry {
1099 map.nodes_with_entry_count = map
1097 map.nodes_with_entry_count = map
1100 .nodes_with_entry_count
1098 .nodes_with_entry_count
1101 .checked_sub(1)
1099 .checked_sub(1)
1102 .expect("nodes_with_entry_count should be >= 0");
1100 .expect("nodes_with_entry_count should be >= 0");
1103 }
1101 }
1104 if dropped.had_copy_source {
1102 if dropped.had_copy_source {
1105 map.nodes_with_copy_source_count = map
1103 map.nodes_with_copy_source_count = map
1106 .nodes_with_copy_source_count
1104 .nodes_with_copy_source_count
1107 .checked_sub(1)
1105 .checked_sub(1)
1108 .expect("nodes_with_copy_source_count should be >= 0");
1106 .expect("nodes_with_copy_source_count should be >= 0");
1109 }
1107 }
1110 } else {
1108 } else {
1111 debug_assert!(!was_tracked);
1109 debug_assert!(!was_tracked);
1112 }
1110 }
1113 Ok(())
1111 Ok(())
1114 })
1112 })
1115 }
1113 }
1116
1114
1117 pub fn has_tracked_dir(
1115 pub fn has_tracked_dir(
1118 &mut self,
1116 &mut self,
1119 directory: &HgPath,
1117 directory: &HgPath,
1120 ) -> Result<bool, DirstateError> {
1118 ) -> Result<bool, DirstateError> {
1121 self.with_dmap_mut(|map| {
1119 self.with_dmap_mut(|map| {
1122 if let Some(node) = map.get_node(directory)? {
1120 if let Some(node) = map.get_node(directory)? {
1123 // A node without a `DirstateEntry` was created to hold child
1121 // A node without a `DirstateEntry` was created to hold child
1124 // nodes, and is therefore a directory.
1122 // nodes, and is therefore a directory.
1125 let state = node.state()?;
1123 let state = node.state()?;
1126 Ok(state.is_none() && node.tracked_descendants_count() > 0)
1124 Ok(state.is_none() && node.tracked_descendants_count() > 0)
1127 } else {
1125 } else {
1128 Ok(false)
1126 Ok(false)
1129 }
1127 }
1130 })
1128 })
1131 }
1129 }
1132
1130
1133 pub fn has_dir(
1131 pub fn has_dir(
1134 &mut self,
1132 &mut self,
1135 directory: &HgPath,
1133 directory: &HgPath,
1136 ) -> Result<bool, DirstateError> {
1134 ) -> Result<bool, DirstateError> {
1137 self.with_dmap_mut(|map| {
1135 self.with_dmap_mut(|map| {
1138 if let Some(node) = map.get_node(directory)? {
1136 if let Some(node) = map.get_node(directory)? {
1139 // A node without a `DirstateEntry` was created to hold child
1137 // A node without a `DirstateEntry` was created to hold child
1140 // nodes, and is therefore a directory.
1138 // nodes, and is therefore a directory.
1141 let state = node.state()?;
1139 let state = node.state()?;
1142 Ok(state.is_none() && node.descendants_with_entry_count() > 0)
1140 Ok(state.is_none() && node.descendants_with_entry_count() > 0)
1143 } else {
1141 } else {
1144 Ok(false)
1142 Ok(false)
1145 }
1143 }
1146 })
1144 })
1147 }
1145 }
1148
1146
1149 #[timed]
1147 #[timed]
1150 pub fn pack_v1(
1148 pub fn pack_v1(
1151 &self,
1149 &self,
1152 parents: DirstateParents,
1150 parents: DirstateParents,
1153 ) -> Result<Vec<u8>, DirstateError> {
1151 ) -> Result<Vec<u8>, DirstateError> {
1154 let map = self.get_map();
1152 let map = self.get_map();
1155 // Optizimation (to be measured?): pre-compute size to avoid `Vec`
1153 // Optizimation (to be measured?): pre-compute size to avoid `Vec`
1156 // reallocations
1154 // reallocations
1157 let mut size = parents.as_bytes().len();
1155 let mut size = parents.as_bytes().len();
1158 for node in map.iter_nodes() {
1156 for node in map.iter_nodes() {
1159 let node = node?;
1157 let node = node?;
1160 if node.entry()?.is_some() {
1158 if node.entry()?.is_some() {
1161 size += packed_entry_size(
1159 size += packed_entry_size(
1162 node.full_path(map.on_disk)?,
1160 node.full_path(map.on_disk)?,
1163 node.copy_source(map.on_disk)?,
1161 node.copy_source(map.on_disk)?,
1164 );
1162 );
1165 }
1163 }
1166 }
1164 }
1167
1165
1168 let mut packed = Vec::with_capacity(size);
1166 let mut packed = Vec::with_capacity(size);
1169 packed.extend(parents.as_bytes());
1167 packed.extend(parents.as_bytes());
1170
1168
1171 for node in map.iter_nodes() {
1169 for node in map.iter_nodes() {
1172 let node = node?;
1170 let node = node?;
1173 if let Some(entry) = node.entry()? {
1171 if let Some(entry) = node.entry()? {
1174 pack_entry(
1172 pack_entry(
1175 node.full_path(map.on_disk)?,
1173 node.full_path(map.on_disk)?,
1176 &entry,
1174 &entry,
1177 node.copy_source(map.on_disk)?,
1175 node.copy_source(map.on_disk)?,
1178 &mut packed,
1176 &mut packed,
1179 );
1177 );
1180 }
1178 }
1181 }
1179 }
1182 Ok(packed)
1180 Ok(packed)
1183 }
1181 }
1184
1182
1185 /// Returns new data and metadata together with whether that data should be
1183 /// Returns new data and metadata together with whether that data should be
1186 /// appended to the existing data file whose content is at
1184 /// appended to the existing data file whose content is at
1187 /// `map.on_disk` (true), instead of written to a new data file
1185 /// `map.on_disk` (true), instead of written to a new data file
1188 /// (false).
1186 /// (false).
1189 #[timed]
1187 #[timed]
1190 pub fn pack_v2(
1188 pub fn pack_v2(
1191 &self,
1189 &self,
1192 can_append: bool,
1190 can_append: bool,
1193 ) -> Result<(Vec<u8>, on_disk::TreeMetadata, bool), DirstateError> {
1191 ) -> Result<(Vec<u8>, on_disk::TreeMetadata, bool), DirstateError> {
1194 let map = self.get_map();
1192 let map = self.get_map();
1195 on_disk::write(map, can_append)
1193 on_disk::write(map, can_append)
1196 }
1194 }
1197
1195
1198 /// `callback` allows the caller to process and do something with the
1196 /// `callback` allows the caller to process and do something with the
1199 /// results of the status. This is needed to do so efficiently (i.e.
1197 /// results of the status. This is needed to do so efficiently (i.e.
1200 /// without cloning the `DirstateStatus` object with its paths) because
1198 /// without cloning the `DirstateStatus` object with its paths) because
1201 /// we need to borrow from `Self`.
1199 /// we need to borrow from `Self`.
1202 pub fn with_status<R>(
1200 pub fn with_status<R>(
1203 &mut self,
1201 &mut self,
1204 matcher: &(dyn Matcher + Sync),
1202 matcher: &(dyn Matcher + Sync),
1205 root_dir: PathBuf,
1203 root_dir: PathBuf,
1206 ignore_files: Vec<PathBuf>,
1204 ignore_files: Vec<PathBuf>,
1207 options: StatusOptions,
1205 options: StatusOptions,
1208 callback: impl for<'r> FnOnce(
1206 callback: impl for<'r> FnOnce(
1209 Result<(DirstateStatus<'r>, Vec<PatternFileWarning>), StatusError>,
1207 Result<(DirstateStatus<'r>, Vec<PatternFileWarning>), StatusError>,
1210 ) -> R,
1208 ) -> R,
1211 ) -> R {
1209 ) -> R {
1212 self.with_dmap_mut(|map| {
1210 self.with_dmap_mut(|map| {
1213 callback(super::status::status(
1211 callback(super::status::status(
1214 map,
1212 map,
1215 matcher,
1213 matcher,
1216 root_dir,
1214 root_dir,
1217 ignore_files,
1215 ignore_files,
1218 options,
1216 options,
1219 ))
1217 ))
1220 })
1218 })
1221 }
1219 }
1222
1220
1223 pub fn copy_map_len(&self) -> usize {
1221 pub fn copy_map_len(&self) -> usize {
1224 let map = self.get_map();
1222 let map = self.get_map();
1225 map.nodes_with_copy_source_count as usize
1223 map.nodes_with_copy_source_count as usize
1226 }
1224 }
1227
1225
1228 pub fn copy_map_iter(&self) -> CopyMapIter<'_> {
1226 pub fn copy_map_iter(&self) -> CopyMapIter<'_> {
1229 let map = self.get_map();
1227 let map = self.get_map();
1230 Box::new(filter_map_results(map.iter_nodes(), move |node| {
1228 Box::new(filter_map_results(map.iter_nodes(), move |node| {
1231 Ok(if let Some(source) = node.copy_source(map.on_disk)? {
1229 Ok(if let Some(source) = node.copy_source(map.on_disk)? {
1232 Some((node.full_path(map.on_disk)?, source))
1230 Some((node.full_path(map.on_disk)?, source))
1233 } else {
1231 } else {
1234 None
1232 None
1235 })
1233 })
1236 }))
1234 }))
1237 }
1235 }
1238
1236
1239 pub fn copy_map_contains_key(
1237 pub fn copy_map_contains_key(
1240 &self,
1238 &self,
1241 key: &HgPath,
1239 key: &HgPath,
1242 ) -> Result<bool, DirstateV2ParseError> {
1240 ) -> Result<bool, DirstateV2ParseError> {
1243 let map = self.get_map();
1241 let map = self.get_map();
1244 Ok(if let Some(node) = map.get_node(key)? {
1242 Ok(if let Some(node) = map.get_node(key)? {
1245 node.has_copy_source()
1243 node.has_copy_source()
1246 } else {
1244 } else {
1247 false
1245 false
1248 })
1246 })
1249 }
1247 }
1250
1248
1251 pub fn copy_map_get(
1249 pub fn copy_map_get(
1252 &self,
1250 &self,
1253 key: &HgPath,
1251 key: &HgPath,
1254 ) -> Result<Option<&HgPath>, DirstateV2ParseError> {
1252 ) -> Result<Option<&HgPath>, DirstateV2ParseError> {
1255 let map = self.get_map();
1253 let map = self.get_map();
1256 if let Some(node) = map.get_node(key)? {
1254 if let Some(node) = map.get_node(key)? {
1257 if let Some(source) = node.copy_source(map.on_disk)? {
1255 if let Some(source) = node.copy_source(map.on_disk)? {
1258 return Ok(Some(source));
1256 return Ok(Some(source));
1259 }
1257 }
1260 }
1258 }
1261 Ok(None)
1259 Ok(None)
1262 }
1260 }
1263
1261
1264 pub fn copy_map_remove(
1262 pub fn copy_map_remove(
1265 &mut self,
1263 &mut self,
1266 key: &HgPath,
1264 key: &HgPath,
1267 ) -> Result<Option<HgPathBuf>, DirstateV2ParseError> {
1265 ) -> Result<Option<HgPathBuf>, DirstateV2ParseError> {
1268 self.with_dmap_mut(|map| {
1266 self.with_dmap_mut(|map| {
1269 let count = &mut map.nodes_with_copy_source_count;
1267 let count = &mut map.nodes_with_copy_source_count;
1270 let unreachable_bytes = &mut map.unreachable_bytes;
1268 let unreachable_bytes = &mut map.unreachable_bytes;
1271 Ok(DirstateMap::get_node_mut(
1269 Ok(DirstateMap::get_node_mut(
1272 map.on_disk,
1270 map.on_disk,
1273 unreachable_bytes,
1271 unreachable_bytes,
1274 &mut map.root,
1272 &mut map.root,
1275 key,
1273 key,
1276 )?
1274 )?
1277 .and_then(|node| {
1275 .and_then(|node| {
1278 if let Some(source) = &node.copy_source {
1276 if let Some(source) = &node.copy_source {
1279 *count -= 1;
1277 *count -= 1;
1280 DirstateMap::count_dropped_path(unreachable_bytes, source);
1278 DirstateMap::count_dropped_path(unreachable_bytes, source);
1281 }
1279 }
1282 node.copy_source.take().map(Cow::into_owned)
1280 node.copy_source.take().map(Cow::into_owned)
1283 }))
1281 }))
1284 })
1282 })
1285 }
1283 }
1286
1284
1287 pub fn copy_map_insert(
1285 pub fn copy_map_insert(
1288 &mut self,
1286 &mut self,
1289 key: HgPathBuf,
1287 key: HgPathBuf,
1290 value: HgPathBuf,
1288 value: HgPathBuf,
1291 ) -> Result<Option<HgPathBuf>, DirstateV2ParseError> {
1289 ) -> Result<Option<HgPathBuf>, DirstateV2ParseError> {
1292 self.with_dmap_mut(|map| {
1290 self.with_dmap_mut(|map| {
1293 let node = DirstateMap::get_or_insert_node(
1291 let node = DirstateMap::get_or_insert_node(
1294 map.on_disk,
1292 map.on_disk,
1295 &mut map.unreachable_bytes,
1293 &mut map.unreachable_bytes,
1296 &mut map.root,
1294 &mut map.root,
1297 &key,
1295 &key,
1298 WithBasename::to_cow_owned,
1296 WithBasename::to_cow_owned,
1299 |_ancestor| {},
1297 |_ancestor| {},
1300 )?;
1298 )?;
1301 if node.copy_source.is_none() {
1299 if node.copy_source.is_none() {
1302 map.nodes_with_copy_source_count += 1
1300 map.nodes_with_copy_source_count += 1
1303 }
1301 }
1304 Ok(node.copy_source.replace(value.into()).map(Cow::into_owned))
1302 Ok(node.copy_source.replace(value.into()).map(Cow::into_owned))
1305 })
1303 })
1306 }
1304 }
1307
1305
1308 pub fn len(&self) -> usize {
1306 pub fn len(&self) -> usize {
1309 let map = self.get_map();
1307 let map = self.get_map();
1310 map.nodes_with_entry_count as usize
1308 map.nodes_with_entry_count as usize
1311 }
1309 }
1312
1310
1313 pub fn contains_key(
1311 pub fn contains_key(
1314 &self,
1312 &self,
1315 key: &HgPath,
1313 key: &HgPath,
1316 ) -> Result<bool, DirstateV2ParseError> {
1314 ) -> Result<bool, DirstateV2ParseError> {
1317 Ok(self.get(key)?.is_some())
1315 Ok(self.get(key)?.is_some())
1318 }
1316 }
1319
1317
1320 pub fn get(
1318 pub fn get(
1321 &self,
1319 &self,
1322 key: &HgPath,
1320 key: &HgPath,
1323 ) -> Result<Option<DirstateEntry>, DirstateV2ParseError> {
1321 ) -> Result<Option<DirstateEntry>, DirstateV2ParseError> {
1324 let map = self.get_map();
1322 let map = self.get_map();
1325 Ok(if let Some(node) = map.get_node(key)? {
1323 Ok(if let Some(node) = map.get_node(key)? {
1326 node.entry()?
1324 node.entry()?
1327 } else {
1325 } else {
1328 None
1326 None
1329 })
1327 })
1330 }
1328 }
1331
1329
1332 pub fn iter(&self) -> StateMapIter<'_> {
1330 pub fn iter(&self) -> StateMapIter<'_> {
1333 let map = self.get_map();
1331 let map = self.get_map();
1334 Box::new(filter_map_results(map.iter_nodes(), move |node| {
1332 Box::new(filter_map_results(map.iter_nodes(), move |node| {
1335 Ok(if let Some(entry) = node.entry()? {
1333 Ok(if let Some(entry) = node.entry()? {
1336 Some((node.full_path(map.on_disk)?, entry))
1334 Some((node.full_path(map.on_disk)?, entry))
1337 } else {
1335 } else {
1338 None
1336 None
1339 })
1337 })
1340 }))
1338 }))
1341 }
1339 }
1342
1340
1343 pub fn iter_tracked_dirs(
1341 pub fn iter_tracked_dirs(
1344 &mut self,
1342 &mut self,
1345 ) -> Result<
1343 ) -> Result<
1346 Box<
1344 Box<
1347 dyn Iterator<Item = Result<&HgPath, DirstateV2ParseError>>
1345 dyn Iterator<Item = Result<&HgPath, DirstateV2ParseError>>
1348 + Send
1346 + Send
1349 + '_,
1347 + '_,
1350 >,
1348 >,
1351 DirstateError,
1349 DirstateError,
1352 > {
1350 > {
1353 let map = self.get_map();
1351 let map = self.get_map();
1354 let on_disk = map.on_disk;
1352 let on_disk = map.on_disk;
1355 Ok(Box::new(filter_map_results(
1353 Ok(Box::new(filter_map_results(
1356 map.iter_nodes(),
1354 map.iter_nodes(),
1357 move |node| {
1355 move |node| {
1358 Ok(if node.tracked_descendants_count() > 0 {
1356 Ok(if node.tracked_descendants_count() > 0 {
1359 Some(node.full_path(on_disk)?)
1357 Some(node.full_path(on_disk)?)
1360 } else {
1358 } else {
1361 None
1359 None
1362 })
1360 })
1363 },
1361 },
1364 )))
1362 )))
1365 }
1363 }
1366
1364
1367 /// Only public because it needs to be exposed to the Python layer.
1365 /// Only public because it needs to be exposed to the Python layer.
1368 /// It is not the full `setparents` logic, only the parts that mutate the
1366 /// It is not the full `setparents` logic, only the parts that mutate the
1369 /// entries.
1367 /// entries.
1370 pub fn setparents_fixup(
1368 pub fn setparents_fixup(
1371 &mut self,
1369 &mut self,
1372 ) -> Result<Vec<(HgPathBuf, HgPathBuf)>, DirstateV2ParseError> {
1370 ) -> Result<Vec<(HgPathBuf, HgPathBuf)>, DirstateV2ParseError> {
1373 // XXX
1371 // XXX
1374 // All the copying and re-querying is quite inefficient, but this is
1372 // All the copying and re-querying is quite inefficient, but this is
1375 // still a lot better than doing it from Python.
1373 // still a lot better than doing it from Python.
1376 //
1374 //
1377 // The better solution is to develop a mechanism for `iter_mut`,
1375 // The better solution is to develop a mechanism for `iter_mut`,
1378 // which will be a lot more involved: we're dealing with a lazy,
1376 // which will be a lot more involved: we're dealing with a lazy,
1379 // append-mostly, tree-like data structure. This will do for now.
1377 // append-mostly, tree-like data structure. This will do for now.
1380 let mut copies = vec![];
1378 let mut copies = vec![];
1381 let mut files_with_p2_info = vec![];
1379 let mut files_with_p2_info = vec![];
1382 for res in self.iter() {
1380 for res in self.iter() {
1383 let (path, entry) = res?;
1381 let (path, entry) = res?;
1384 if entry.p2_info() {
1382 if entry.p2_info() {
1385 files_with_p2_info.push(path.to_owned())
1383 files_with_p2_info.push(path.to_owned())
1386 }
1384 }
1387 }
1385 }
1388 self.with_dmap_mut(|map| {
1386 self.with_dmap_mut(|map| {
1389 for path in files_with_p2_info.iter() {
1387 for path in files_with_p2_info.iter() {
1390 let node = map.get_or_insert(path)?;
1388 let node = map.get_or_insert(path)?;
1391 let entry =
1389 let entry =
1392 node.data.as_entry_mut().expect("entry should exist");
1390 node.data.as_entry_mut().expect("entry should exist");
1393 entry.drop_merge_data();
1391 entry.drop_merge_data();
1394 if let Some(source) = node.copy_source.take().as_deref() {
1392 if let Some(source) = node.copy_source.take().as_deref() {
1395 copies.push((path.to_owned(), source.to_owned()));
1393 copies.push((path.to_owned(), source.to_owned()));
1396 }
1394 }
1397 }
1395 }
1398 Ok(copies)
1396 Ok(copies)
1399 })
1397 })
1400 }
1398 }
1401
1399
1402 pub fn debug_iter(
1400 pub fn debug_iter(
1403 &self,
1401 &self,
1404 all: bool,
1402 all: bool,
1405 ) -> Box<
1403 ) -> Box<
1406 dyn Iterator<
1404 dyn Iterator<
1407 Item = Result<
1405 Item = Result<
1408 (&HgPath, (u8, i32, i32, i32)),
1406 (&HgPath, (u8, i32, i32, i32)),
1409 DirstateV2ParseError,
1407 DirstateV2ParseError,
1410 >,
1408 >,
1411 > + Send
1409 > + Send
1412 + '_,
1410 + '_,
1413 > {
1411 > {
1414 let map = self.get_map();
1412 let map = self.get_map();
1415 Box::new(filter_map_results(map.iter_nodes(), move |node| {
1413 Box::new(filter_map_results(map.iter_nodes(), move |node| {
1416 let debug_tuple = if let Some(entry) = node.entry()? {
1414 let debug_tuple = if let Some(entry) = node.entry()? {
1417 entry.debug_tuple()
1415 entry.debug_tuple()
1418 } else if !all {
1416 } else if !all {
1419 return Ok(None);
1417 return Ok(None);
1420 } else if let Some(mtime) = node.cached_directory_mtime()? {
1418 } else if let Some(mtime) = node.cached_directory_mtime()? {
1421 (b' ', 0, -1, mtime.truncated_seconds() as i32)
1419 (b' ', 0, -1, mtime.truncated_seconds() as i32)
1422 } else {
1420 } else {
1423 (b' ', 0, -1, -1)
1421 (b' ', 0, -1, -1)
1424 };
1422 };
1425 Ok(Some((node.full_path(map.on_disk)?, debug_tuple)))
1423 Ok(Some((node.full_path(map.on_disk)?, debug_tuple)))
1426 }))
1424 }))
1427 }
1425 }
1428 }
1426 }
General Comments 0
You need to be logged in to leave comments. Login now