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