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