comparison rust/hg-core/src/dirstate_tree/dirstate_map.rs @ 47352:9d58e54b5966

dirstate-v2: Drop parent directory cache when removing a dirstate node The premise of the directory cache is that the dirstate contains child nodes for every entry that `read_dir` would return. When removing nodes, that may not be the case anymore so the cache should be invalidated. Differential Revision: https://phab.mercurial-scm.org/D10829
author Simon Sapin <simon.sapin@octobus.net>
date Tue, 01 Jun 2021 16:55:59 +0200
parents 3b9914b28133
children 0ef8231e413f
comparison
equal deleted inserted replaced
47351:3b9914b28133 47352:9d58e54b5966
710 struct Dropped { 710 struct Dropped {
711 was_tracked: bool, 711 was_tracked: bool,
712 had_entry: bool, 712 had_entry: bool,
713 had_copy_source: bool, 713 had_copy_source: bool,
714 } 714 }
715
716 /// If this returns `Ok(Some((dropped, removed)))`, then
717 ///
718 /// * `dropped` is about the leaf node that was at `filename`
719 /// * `removed` is whether this particular level of recursion just
720 /// removed a node in `nodes`.
715 fn recur<'on_disk>( 721 fn recur<'on_disk>(
716 on_disk: &'on_disk [u8], 722 on_disk: &'on_disk [u8],
717 nodes: &mut ChildNodes<'on_disk>, 723 nodes: &mut ChildNodes<'on_disk>,
718 path: &HgPath, 724 path: &HgPath,
719 ) -> Result<Option<Dropped>, DirstateV2ParseError> { 725 ) -> Result<Option<(Dropped, bool)>, DirstateV2ParseError> {
720 let (first_path_component, rest_of_path) = 726 let (first_path_component, rest_of_path) =
721 path.split_first_component(); 727 path.split_first_component();
722 let node = if let Some(node) = 728 let node = if let Some(node) =
723 nodes.make_mut(on_disk)?.get_mut(first_path_component) 729 nodes.make_mut(on_disk)?.get_mut(first_path_component)
724 { 730 {
726 } else { 732 } else {
727 return Ok(None); 733 return Ok(None);
728 }; 734 };
729 let dropped; 735 let dropped;
730 if let Some(rest) = rest_of_path { 736 if let Some(rest) = rest_of_path {
731 if let Some(d) = recur(on_disk, &mut node.children, rest)? { 737 if let Some((d, removed)) =
738 recur(on_disk, &mut node.children, rest)?
739 {
732 dropped = d; 740 dropped = d;
733 if dropped.was_tracked { 741 if dropped.was_tracked {
734 node.tracked_descendants_count -= 1; 742 node.tracked_descendants_count -= 1;
743 }
744
745 // Directory caches must be invalidated when removing a
746 // child node
747 if removed {
748 if let NodeData::CachedDirectory { .. } = &node.data {
749 node.data = NodeData::None
750 }
735 } 751 }
736 } else { 752 } else {
737 return Ok(None); 753 return Ok(None);
738 } 754 }
739 } else { 755 } else {
750 had_copy_source: node.copy_source.take().is_some(), 766 had_copy_source: node.copy_source.take().is_some(),
751 }; 767 };
752 } 768 }
753 // After recursion, for both leaf (rest_of_path is None) nodes and 769 // After recursion, for both leaf (rest_of_path is None) nodes and
754 // parent nodes, remove a node if it just became empty. 770 // parent nodes, remove a node if it just became empty.
755 if !node.data.has_entry() 771 let remove = !node.data.has_entry()
756 && node.copy_source.is_none() 772 && node.copy_source.is_none()
757 && node.children.is_empty() 773 && node.children.is_empty();
758 { 774 if remove {
759 nodes.make_mut(on_disk)?.remove(first_path_component); 775 nodes.make_mut(on_disk)?.remove(first_path_component);
760 } 776 }
761 Ok(Some(dropped)) 777 Ok(Some((dropped, remove)))
762 } 778 }
763 779
764 if let Some(dropped) = recur(self.on_disk, &mut self.root, filename)? { 780 if let Some((dropped, _removed)) =
781 recur(self.on_disk, &mut self.root, filename)?
782 {
765 if dropped.had_entry { 783 if dropped.had_entry {
766 self.nodes_with_entry_count -= 1 784 self.nodes_with_entry_count -= 1
767 } 785 }
768 if dropped.had_copy_source { 786 if dropped.had_copy_source {
769 self.nodes_with_copy_source_count -= 1 787 self.nodes_with_copy_source_count -= 1