##// END OF EJS Templates
dirstate-tree: Add add_file, remove_file, and drop_file...
Simon Sapin -
r47877:7dfc598d default
parent child Browse files
Show More
@@ -1,609 +1,642 b''
1 1 use bytes_cast::BytesCast;
2 2 use std::path::PathBuf;
3 3 use std::{collections::BTreeMap, convert::TryInto};
4 4
5 5 use super::path_with_basename::WithBasename;
6 6 use crate::dirstate::parsers::clear_ambiguous_mtime;
7 7 use crate::dirstate::parsers::pack_entry;
8 8 use crate::dirstate::parsers::packed_entry_size;
9 9 use crate::dirstate::parsers::parse_dirstate_entries;
10 10 use crate::dirstate::parsers::parse_dirstate_parents;
11 11 use crate::dirstate::parsers::Timestamp;
12 12 use crate::matchers::Matcher;
13 13 use crate::revlog::node::NULL_NODE;
14 14 use crate::utils::hg_path::{HgPath, HgPathBuf};
15 15 use crate::CopyMapIter;
16 16 use crate::DirstateEntry;
17 17 use crate::DirstateError;
18 18 use crate::DirstateMapError;
19 19 use crate::DirstateParents;
20 20 use crate::DirstateStatus;
21 21 use crate::EntryState;
22 22 use crate::FastHashMap;
23 23 use crate::HgPathCow;
24 24 use crate::PatternFileWarning;
25 25 use crate::StateMapIter;
26 26 use crate::StatusError;
27 27 use crate::StatusOptions;
28 28
29 29 pub struct DirstateMap {
30 30 parents: Option<DirstateParents>,
31 31 dirty_parents: bool,
32 32 root: ChildNodes,
33 33
34 34 /// Number of nodes anywhere in the tree that have `.entry.is_some()`.
35 35 nodes_with_entry_count: usize,
36 36
37 37 /// Number of nodes anywhere in the tree that have
38 38 /// `.copy_source.is_some()`.
39 39 nodes_with_copy_source_count: usize,
40 40 }
41 41
42 42 /// Using a plain `HgPathBuf` of the full path from the repository root as a
43 43 /// map key would also work: all paths in a given map have the same parent
44 44 /// path, so comparing full paths gives the same result as comparing base
45 45 /// names. However `BTreeMap` would waste time always re-comparing the same
46 46 /// string prefix.
47 47 type ChildNodes = BTreeMap<WithBasename<HgPathBuf>, Node>;
48 48
49 49 /// Represents a file or a directory
50 50 #[derive(Default)]
51 51 struct Node {
52 52 /// `None` for directories
53 53 entry: Option<DirstateEntry>,
54 54
55 55 copy_source: Option<HgPathBuf>,
56 56
57 57 children: ChildNodes,
58 58
59 59 /// How many (non-inclusive) descendants of this node are tracked files
60 60 tracked_descendants_count: usize,
61 61 }
62 62
63 63 impl Node {
64 64 /// Whether this node has a `DirstateEntry` with `.state.is_tracked()`
65 65 fn is_tracked_file(&self) -> bool {
66 66 if let Some(entry) = &self.entry {
67 67 entry.state.is_tracked()
68 68 } else {
69 69 false
70 70 }
71 71 }
72 72 }
73 73
74 74 /// `(full_path, entry, copy_source)`
75 75 type NodeDataMut<'a> = (
76 76 &'a WithBasename<HgPathBuf>,
77 77 &'a mut Option<DirstateEntry>,
78 78 &'a mut Option<HgPathBuf>,
79 79 );
80 80
81 81 impl DirstateMap {
82 82 pub fn new() -> Self {
83 83 Self {
84 84 parents: None,
85 85 dirty_parents: false,
86 86 root: ChildNodes::new(),
87 87 nodes_with_entry_count: 0,
88 88 nodes_with_copy_source_count: 0,
89 89 }
90 90 }
91 91
92 92 fn get_node(&self, path: &HgPath) -> Option<&Node> {
93 93 let mut children = &self.root;
94 94 let mut components = path.components();
95 95 let mut component =
96 96 components.next().expect("expected at least one components");
97 97 loop {
98 98 let child = children.get(component)?;
99 99 if let Some(next_component) = components.next() {
100 100 component = next_component;
101 101 children = &child.children;
102 102 } else {
103 103 return Some(child);
104 104 }
105 105 }
106 106 }
107 107
108 108 /// Returns a mutable reference to the node at `path` if it exists
109 109 ///
110 110 /// This takes `root` instead of `&mut self` so that callers can mutate
111 111 /// other fields while the returned borrow is still valid
112 112 fn get_node_mut<'tree>(
113 113 root: &'tree mut ChildNodes,
114 114 path: &HgPath,
115 115 ) -> Option<&'tree mut Node> {
116 116 Self::each_and_get(root, path, |_| {})
117 117 }
118 118
119 119 /// Call `each` for each ancestor node of the one at `path` (not including
120 120 /// that node itself), starting from nearest the root.
121 121 ///
122 122 /// Panics (possibly after some calls to `each`) if there is no node at
123 123 /// `path`.
124 124 fn for_each_ancestor_node<'tree>(
125 125 &mut self,
126 126 path: &HgPath,
127 127 each: impl FnMut(&mut Node),
128 128 ) {
129 129 let parent = path.parent();
130 130 if !parent.is_empty() {
131 131 Self::each_and_get(&mut self.root, parent, each)
132 132 .expect("missing dirstate node");
133 133 }
134 134 }
135 135
136 136 /// Common implementation detail of `get_node_mut` and
137 137 /// `for_each_ancestor_node`
138 138 fn each_and_get<'tree>(
139 139 root: &'tree mut ChildNodes,
140 140 path: &HgPath,
141 141 mut each: impl FnMut(&mut Node),
142 142 ) -> Option<&'tree mut Node> {
143 143 let mut children = root;
144 144 let mut components = path.components();
145 145 let mut component =
146 146 components.next().expect("expected at least one components");
147 147 loop {
148 148 let child = children.get_mut(component)?;
149 149 each(child);
150 150 if let Some(next_component) = components.next() {
151 151 component = next_component;
152 152 children = &mut child.children;
153 153 } else {
154 154 return Some(child);
155 155 }
156 156 }
157 157 }
158 158
159 159 fn get_or_insert_node<'tree>(
160 160 root: &'tree mut ChildNodes,
161 161 path: &HgPath,
162 162 ) -> &'tree mut Node {
163 163 let mut child_nodes = root;
164 164 let mut inclusive_ancestor_paths =
165 165 WithBasename::inclusive_ancestors_of(path);
166 166 let mut ancestor_path = inclusive_ancestor_paths
167 167 .next()
168 168 .expect("expected at least one inclusive ancestor");
169 169 loop {
170 170 // TODO: can we avoid double lookup in all cases without allocating
171 171 // an owned key in cases where the map already contains that key?
172 172 let child_node =
173 173 if child_nodes.contains_key(ancestor_path.base_name()) {
174 174 child_nodes.get_mut(ancestor_path.base_name()).unwrap()
175 175 } else {
176 176 // This is always a vacant entry, using `.entry()` lets us
177 177 // return a `&mut Node` of the newly-inserted node without
178 178 // yet another lookup. `BTreeMap::insert` doesn’t do this.
179 179 child_nodes.entry(ancestor_path.to_owned()).or_default()
180 180 };
181 181 if let Some(next) = inclusive_ancestor_paths.next() {
182 182 ancestor_path = next;
183 183 child_nodes = &mut child_node.children;
184 184 } else {
185 185 return child_node;
186 186 }
187 187 }
188 188 }
189 189
190 190 /// The meaning of `new_copy_source` is:
191 191 ///
192 192 /// * `Some(Some(x))`: set `Node::copy_source` to `Some(x)`
193 193 /// * `Some(None)`: set `Node::copy_source` to `None`
194 194 /// * `None`: leave `Node::copy_source` unchanged
195 195 fn add_file_node(
196 196 &mut self,
197 197 path: &HgPath,
198 198 new_entry: DirstateEntry,
199 199 new_copy_source: Option<Option<HgPathBuf>>,
200 200 ) {
201 201 let node = Self::get_or_insert_node(&mut self.root, path);
202 202 if node.entry.is_none() {
203 203 self.nodes_with_entry_count += 1
204 204 }
205 205 if let Some(source) = &new_copy_source {
206 206 if node.copy_source.is_none() && source.is_some() {
207 207 self.nodes_with_copy_source_count += 1
208 208 }
209 209 if node.copy_source.is_some() && source.is_none() {
210 210 self.nodes_with_copy_source_count -= 1
211 211 }
212 212 }
213 213 let tracked_count_increment =
214 214 match (node.is_tracked_file(), new_entry.state.is_tracked()) {
215 215 (false, true) => 1,
216 216 (true, false) => -1,
217 217 _ => 0,
218 218 };
219 219
220 220 node.entry = Some(new_entry);
221 221 if let Some(source) = new_copy_source {
222 222 node.copy_source = source
223 223 }
224 224 // Borrow of `self.root` through `node` ends here
225 225
226 226 match tracked_count_increment {
227 227 1 => self.for_each_ancestor_node(path, |node| {
228 228 node.tracked_descendants_count += 1
229 229 }),
230 230 // We can’t use `+= -1` because the counter is unsigned
231 231 -1 => self.for_each_ancestor_node(path, |node| {
232 232 node.tracked_descendants_count -= 1
233 233 }),
234 234 _ => {}
235 235 }
236 236 }
237 237
238 238 fn iter_nodes<'a>(
239 239 &'a self,
240 240 ) -> impl Iterator<Item = (&'a WithBasename<HgPathBuf>, &'a Node)> + 'a
241 241 {
242 242 // Depth first tree traversal.
243 243 //
244 244 // If we could afford internal iteration and recursion,
245 245 // this would look like:
246 246 //
247 247 // ```
248 248 // fn traverse_children(
249 249 // children: &ChildNodes,
250 250 // each: &mut impl FnMut(&Node),
251 251 // ) {
252 252 // for child in children.values() {
253 253 // traverse_children(&child.children, each);
254 254 // each(child);
255 255 // }
256 256 // }
257 257 // ```
258 258 //
259 259 // However we want an external iterator and therefore can’t use the
260 260 // call stack. Use an explicit stack instead:
261 261 let mut stack = Vec::new();
262 262 let mut iter = self.root.iter();
263 263 std::iter::from_fn(move || {
264 264 while let Some((key, child_node)) = iter.next() {
265 265 // Pseudo-recursion
266 266 let new_iter = child_node.children.iter();
267 267 let old_iter = std::mem::replace(&mut iter, new_iter);
268 268 stack.push((key, child_node, old_iter));
269 269 }
270 270 // Found the end of a `children.iter()` iterator.
271 271 if let Some((key, child_node, next_iter)) = stack.pop() {
272 272 // "Return" from pseudo-recursion by restoring state from the
273 273 // explicit stack
274 274 iter = next_iter;
275 275
276 276 Some((key, child_node))
277 277 } else {
278 278 // Reached the bottom of the stack, we’re done
279 279 None
280 280 }
281 281 })
282 282 }
283 283
284 284 /// Mutable iterator for the `(entry, copy source)` of each node.
285 285 ///
286 286 /// It would not be safe to yield mutable references to nodes themeselves
287 287 /// with `-> impl Iterator<Item = &mut Node>` since child nodes are
288 288 /// reachable from their ancestor nodes, potentially creating multiple
289 289 /// `&mut` references to a given node.
290 290 fn iter_node_data_mut<'a>(
291 291 &'a mut self,
292 292 ) -> impl Iterator<Item = NodeDataMut<'a>> + 'a {
293 293 // Explict stack for pseudo-recursion, see `iter_nodes` above.
294 294 let mut stack = Vec::new();
295 295 let mut iter = self.root.iter_mut();
296 296 std::iter::from_fn(move || {
297 297 while let Some((key, child_node)) = iter.next() {
298 298 // Pseudo-recursion
299 299 let data =
300 300 (key, &mut child_node.entry, &mut child_node.copy_source);
301 301 let new_iter = child_node.children.iter_mut();
302 302 let old_iter = std::mem::replace(&mut iter, new_iter);
303 303 stack.push((data, old_iter));
304 304 }
305 305 // Found the end of a `children.values_mut()` iterator.
306 306 if let Some((data, next_iter)) = stack.pop() {
307 307 // "Return" from pseudo-recursion by restoring state from the
308 308 // explicit stack
309 309 iter = next_iter;
310 310
311 311 Some(data)
312 312 } else {
313 313 // Reached the bottom of the stack, we’re done
314 314 None
315 315 }
316 316 })
317 317 }
318 318 }
319 319
320 320 impl super::dispatch::DirstateMapMethods for DirstateMap {
321 321 fn clear(&mut self) {
322 322 self.set_parents(&DirstateParents {
323 323 p1: NULL_NODE,
324 324 p2: NULL_NODE,
325 325 });
326 326 self.root.clear();
327 327 self.nodes_with_entry_count = 0;
328 328 self.nodes_with_copy_source_count = 0;
329 329 }
330 330
331 331 fn add_file(
332 332 &mut self,
333 _filename: &HgPath,
333 filename: &HgPath,
334 334 _old_state: EntryState,
335 _entry: DirstateEntry,
335 entry: DirstateEntry,
336 336 ) -> Result<(), DirstateMapError> {
337 todo!()
337 self.add_file_node(filename, entry, None);
338 Ok(())
338 339 }
339 340
340 341 fn remove_file(
341 342 &mut self,
342 _filename: &HgPath,
343 filename: &HgPath,
343 344 _old_state: EntryState,
344 _size: i32,
345 size: i32,
345 346 ) -> Result<(), DirstateMapError> {
346 todo!()
347 let entry = DirstateEntry {
348 state: EntryState::Removed,
349 mode: 0,
350 size,
351 mtime: 0,
352 };
353 self.add_file_node(filename, entry, None);
354 Ok(())
347 355 }
348 356
349 357 fn drop_file(
350 358 &mut self,
351 _filename: &HgPath,
359 filename: &HgPath,
352 360 _old_state: EntryState,
353 361 ) -> Result<bool, DirstateMapError> {
354 todo!()
362 if let Some(node) = Self::get_node_mut(&mut self.root, filename) {
363 let was_tracked = node.is_tracked_file();
364 let had_entry = node.entry.is_some();
365 let had_copy_source = node.copy_source.is_some();
366
367 // TODO: this leaves in the tree a "non-file" node. Should we
368 // remove the node instead, together with ancestor nodes for
369 // directories that become empty?
370 node.entry = None;
371 node.copy_source = None;
372
373 if had_entry {
374 self.nodes_with_entry_count -= 1
375 }
376 if had_copy_source {
377 self.nodes_with_copy_source_count -= 1
378 }
379 if was_tracked {
380 self.for_each_ancestor_node(filename, |node| {
381 node.tracked_descendants_count -= 1
382 })
383 }
384 Ok(had_entry)
385 } else {
386 Ok(false)
387 }
355 388 }
356 389
357 390 fn clear_ambiguous_times(&mut self, filenames: Vec<HgPathBuf>, now: i32) {
358 391 for filename in filenames {
359 392 if let Some(node) = Self::get_node_mut(&mut self.root, &filename) {
360 393 if let Some(entry) = node.entry.as_mut() {
361 394 clear_ambiguous_mtime(entry, now);
362 395 }
363 396 }
364 397 }
365 398 }
366 399
367 400 fn non_normal_entries_contains(&mut self, _key: &HgPath) -> bool {
368 401 todo!()
369 402 }
370 403
371 404 fn non_normal_entries_remove(&mut self, _key: &HgPath) -> bool {
372 405 todo!()
373 406 }
374 407
375 408 fn non_normal_or_other_parent_paths(
376 409 &mut self,
377 410 ) -> Box<dyn Iterator<Item = &HgPathBuf> + '_> {
378 411 todo!()
379 412 }
380 413
381 414 fn set_non_normal_other_parent_entries(&mut self, _force: bool) {
382 415 todo!()
383 416 }
384 417
385 418 fn iter_non_normal_paths(
386 419 &mut self,
387 420 ) -> Box<dyn Iterator<Item = &HgPathBuf> + Send + '_> {
388 421 todo!()
389 422 }
390 423
391 424 fn iter_non_normal_paths_panic(
392 425 &self,
393 426 ) -> Box<dyn Iterator<Item = &HgPathBuf> + Send + '_> {
394 427 todo!()
395 428 }
396 429
397 430 fn iter_other_parent_paths(
398 431 &mut self,
399 432 ) -> Box<dyn Iterator<Item = &HgPathBuf> + Send + '_> {
400 433 todo!()
401 434 }
402 435
403 436 fn has_tracked_dir(
404 437 &mut self,
405 438 directory: &HgPath,
406 439 ) -> Result<bool, DirstateMapError> {
407 440 if let Some(node) = self.get_node(directory) {
408 441 // A node without a `DirstateEntry` was created to hold child
409 442 // nodes, and is therefore a directory.
410 443 Ok(node.entry.is_none() && node.tracked_descendants_count > 0)
411 444 } else {
412 445 Ok(false)
413 446 }
414 447 }
415 448
416 449 fn has_dir(
417 450 &mut self,
418 451 directory: &HgPath,
419 452 ) -> Result<bool, DirstateMapError> {
420 453 if let Some(node) = self.get_node(directory) {
421 454 // A node without a `DirstateEntry` was created to hold child
422 455 // nodes, and is therefore a directory.
423 456 Ok(node.entry.is_none())
424 457 } else {
425 458 Ok(false)
426 459 }
427 460 }
428 461
429 462 fn parents(
430 463 &mut self,
431 464 file_contents: &[u8],
432 465 ) -> Result<&DirstateParents, DirstateError> {
433 466 if self.parents.is_none() {
434 467 let parents = if !file_contents.is_empty() {
435 468 parse_dirstate_parents(file_contents)?.clone()
436 469 } else {
437 470 DirstateParents {
438 471 p1: NULL_NODE,
439 472 p2: NULL_NODE,
440 473 }
441 474 };
442 475 self.parents = Some(parents);
443 476 }
444 477 Ok(self.parents.as_ref().unwrap())
445 478 }
446 479
447 480 fn set_parents(&mut self, parents: &DirstateParents) {
448 481 self.parents = Some(parents.clone());
449 482 self.dirty_parents = true;
450 483 }
451 484
452 485 fn read<'a>(
453 486 &mut self,
454 487 file_contents: &'a [u8],
455 488 ) -> Result<Option<&'a DirstateParents>, DirstateError> {
456 489 if file_contents.is_empty() {
457 490 return Ok(None);
458 491 }
459 492
460 493 let parents = parse_dirstate_entries(
461 494 file_contents,
462 495 |path, entry, copy_source| {
463 496 self.add_file_node(
464 497 path,
465 498 *entry,
466 499 Some(copy_source.map(HgPath::to_owned)),
467 500 )
468 501 },
469 502 )?;
470 503
471 504 if !self.dirty_parents {
472 505 self.set_parents(parents);
473 506 }
474 507
475 508 Ok(Some(parents))
476 509 }
477 510
478 511 fn pack(
479 512 &mut self,
480 513 parents: DirstateParents,
481 514 now: Timestamp,
482 515 ) -> Result<Vec<u8>, DirstateError> {
483 516 // Optizimation (to be measured?): pre-compute size to avoid `Vec`
484 517 // reallocations
485 518 let mut size = parents.as_bytes().len();
486 519 for (path, node) in self.iter_nodes() {
487 520 if node.entry.is_some() {
488 521 size += packed_entry_size(
489 522 path.full_path(),
490 523 node.copy_source.as_ref(),
491 524 )
492 525 }
493 526 }
494 527
495 528 let mut packed = Vec::with_capacity(size);
496 529 packed.extend(parents.as_bytes());
497 530
498 531 let now: i32 = now.0.try_into().expect("time overflow");
499 532 for (path, opt_entry, copy_source) in self.iter_node_data_mut() {
500 533 if let Some(entry) = opt_entry {
501 534 clear_ambiguous_mtime(entry, now);
502 535 pack_entry(
503 536 path.full_path(),
504 537 entry,
505 538 copy_source.as_ref(),
506 539 &mut packed,
507 540 );
508 541 }
509 542 }
510 543 self.dirty_parents = false;
511 544 Ok(packed)
512 545 }
513 546
514 547 fn build_file_fold_map(&mut self) -> &FastHashMap<HgPathBuf, HgPathBuf> {
515 548 todo!()
516 549 }
517 550
518 551 fn set_all_dirs(&mut self) -> Result<(), DirstateMapError> {
519 552 // Do nothing, this `DirstateMap` does not a separate `all_dirs` that
520 553 // needs to be recomputed
521 554 Ok(())
522 555 }
523 556
524 557 fn set_dirs(&mut self) -> Result<(), DirstateMapError> {
525 558 // Do nothing, this `DirstateMap` does not a separate `dirs` that needs
526 559 // to be recomputed
527 560 Ok(())
528 561 }
529 562
530 563 fn status<'a>(
531 564 &'a self,
532 565 _matcher: &'a (dyn Matcher + Sync),
533 566 _root_dir: PathBuf,
534 567 _ignore_files: Vec<PathBuf>,
535 568 _options: StatusOptions,
536 569 ) -> Result<
537 570 (
538 571 (Vec<HgPathCow<'a>>, DirstateStatus<'a>),
539 572 Vec<PatternFileWarning>,
540 573 ),
541 574 StatusError,
542 575 > {
543 576 todo!()
544 577 }
545 578
546 579 fn copy_map_len(&self) -> usize {
547 580 self.nodes_with_copy_source_count
548 581 }
549 582
550 583 fn copy_map_iter(&self) -> CopyMapIter<'_> {
551 584 Box::new(self.iter_nodes().filter_map(|(path, node)| {
552 585 node.copy_source
553 586 .as_ref()
554 587 .map(|copy_source| (path.full_path(), copy_source))
555 588 }))
556 589 }
557 590
558 591 fn copy_map_contains_key(&self, key: &HgPath) -> bool {
559 592 if let Some(node) = self.get_node(key) {
560 593 node.copy_source.is_some()
561 594 } else {
562 595 false
563 596 }
564 597 }
565 598
566 599 fn copy_map_get(&self, key: &HgPath) -> Option<&HgPathBuf> {
567 600 self.get_node(key)?.copy_source.as_ref()
568 601 }
569 602
570 603 fn copy_map_remove(&mut self, key: &HgPath) -> Option<HgPathBuf> {
571 604 let count = &mut self.nodes_with_copy_source_count;
572 605 Self::get_node_mut(&mut self.root, key).and_then(|node| {
573 606 if node.copy_source.is_some() {
574 607 *count -= 1
575 608 }
576 609 node.copy_source.take()
577 610 })
578 611 }
579 612
580 613 fn copy_map_insert(
581 614 &mut self,
582 615 key: HgPathBuf,
583 616 value: HgPathBuf,
584 617 ) -> Option<HgPathBuf> {
585 618 let node = Self::get_or_insert_node(&mut self.root, &key);
586 619 if node.copy_source.is_none() {
587 620 self.nodes_with_copy_source_count += 1
588 621 }
589 622 node.copy_source.replace(value)
590 623 }
591 624
592 625 fn len(&self) -> usize {
593 626 self.nodes_with_entry_count
594 627 }
595 628
596 629 fn contains_key(&self, key: &HgPath) -> bool {
597 630 self.get(key).is_some()
598 631 }
599 632
600 633 fn get(&self, key: &HgPath) -> Option<&DirstateEntry> {
601 634 self.get_node(key)?.entry.as_ref()
602 635 }
603 636
604 637 fn iter(&self) -> StateMapIter<'_> {
605 638 Box::new(self.iter_nodes().filter_map(|(path, node)| {
606 639 node.entry.as_ref().map(|entry| (path.full_path(), entry))
607 640 }))
608 641 }
609 642 }
General Comments 0
You need to be logged in to leave comments. Login now