From f29c42904ea081c9729b495019974e19043b4220 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Mon, 19 Apr 2021 12:53:02 +0200 Subject: [PATCH] Recompute the PathEntry for each file under a re-parented subtree --- zed/src/worktree.rs | 76 ++++++++++++++++++++++++++++++++++----------- 1 file changed, 58 insertions(+), 18 deletions(-) diff --git a/zed/src/worktree.rs b/zed/src/worktree.rs index 0093c6a213..5515043b4f 100644 --- a/zed/src/worktree.rs +++ b/zed/src/worktree.rs @@ -255,7 +255,7 @@ impl Snapshot { fn reparent_entry( &mut self, child_inode: u64, - new_filename: Option<&OsStr>, + new_path: Option<&Path>, old_parent_inode: Option, new_parent_inode: Option, ) { @@ -282,11 +282,11 @@ impl Snapshot { let mut old_parent_entry = None; let mut new_parent_entry = None; for removed_entry in removed_entries { - if removed_entry.ino() == child_inode { + if removed_entry.inode() == child_inode { child_entry = Some(removed_entry); - } else if Some(removed_entry.ino()) == old_parent_inode { + } else if Some(removed_entry.inode()) == old_parent_inode { old_parent_entry = Some(removed_entry); - } else if Some(removed_entry.ino()) == new_parent_inode { + } else if Some(removed_entry.inode()) == new_parent_inode { new_parent_entry = Some(removed_entry); } } @@ -294,12 +294,47 @@ impl Snapshot { // Update the child entry's parent. let mut child_entry = child_entry.expect("cannot reparent non-existent entry"); child_entry.set_parent(new_parent_inode); - if let Some(new_filename) = new_filename { - child_entry.set_name(new_filename); - } - insertions.push(Edit::Insert(child_entry)); + if let Some(new_path) = new_path { + let new_path = new_path.strip_prefix(self.path.parent().unwrap()).unwrap(); + child_entry.set_name(new_path.file_name().unwrap()); - // Remove the child entry from it's old parent's children. + // Recompute the PathEntry for each file under this subtree. + let mut stack = Vec::new(); + stack.push((child_entry, new_path.parent().unwrap().to_path_buf())); + while let Some((mut entry, mut new_path)) = stack.pop() { + new_path.push(entry.name()); + match &mut entry { + Entry::Dir { + children, inode, .. + } => { + for child_inode in children.as_ref() { + let child_entry = self.entries.get(child_inode).unwrap(); + stack.push((child_entry.clone(), new_path.clone())); + } + + // Descendant directories don't need to be mutated because their properties + // haven't changed, so only re-insert this directory if it is the entry we + // were reparenting. + if *inode == child_inode { + insertions.push(Edit::Insert(entry)); + } + } + Entry::File { + inode, + is_ignored, + path, + .. + } => { + *path = PathEntry::new(*inode, &new_path, *is_ignored); + insertions.push(Edit::Insert(entry)); + } + } + } + } else { + insertions.push(Edit::Insert(child_entry)); + } + + // Remove the child entry from its old parent's children. if let Some(mut old_parent_entry) = old_parent_entry { if let Entry::Dir { children, .. } = &mut old_parent_entry { *children = children @@ -313,7 +348,7 @@ impl Snapshot { } } - // Add the child entry to it's new parent's children. + // Add the child entry to its new parent's children. if let Some(mut new_parent_entry) = new_parent_entry { if let Entry::Dir { children, .. } = &mut new_parent_entry { *children = children @@ -410,7 +445,7 @@ pub enum Entry { } impl Entry { - fn ino(&self) -> u64 { + fn inode(&self) -> u64 { match self { Entry::Dir { inode, .. } => *inode, Entry::File { inode, .. } => *inode, @@ -455,7 +490,7 @@ impl sum_tree::Item for Entry { fn summary(&self) -> Self::Summary { EntrySummary { - max_ino: self.ino(), + max_ino: self.inode(), file_count: if matches!(self, Self::File { .. }) { 1 } else { @@ -469,7 +504,7 @@ impl sum_tree::KeyedItem for Entry { type Key = u64; fn key(&self) -> Self::Key { - self.ino() + self.inode() } } @@ -753,13 +788,13 @@ impl BackgroundScanner { // If this path currently exists on the filesystem, then ensure that the snapshot's // entry for this path is up-to-date. Ok(Some((fs_entry, ignore))) => { - let fs_inode = fs_entry.ino(); + let fs_inode = fs_entry.inode(); let fs_parent_inode = fs_entry.parent(); // If the snapshot already contains an entry for this path, then ensure that the // entry has the correct inode and parent. if let Some(snapshot_entry) = snapshot_entry { - let snapshot_inode = snapshot_entry.ino(); + let snapshot_inode = snapshot_entry.inode(); let snapshot_parent_inode = snapshot_entry.parent(); // If the snapshot entry already matches the filesystem, then skip to the @@ -781,7 +816,7 @@ impl BackgroundScanner { let snapshot_parent_inode = snapshot_entry_for_inode.parent(); snapshot.reparent_entry( fs_inode, - path.file_name(), + Some(&path), snapshot_parent_inode, fs_parent_inode, ); @@ -825,7 +860,7 @@ impl BackgroundScanner { // If this path no longer exists on the filesystem, then remove it from the snapshot. Ok(None) => { if let Some(snapshot_entry) = snapshot_entry { - let snapshot_inode = snapshot_entry.ino(); + let snapshot_inode = snapshot_entry.inode(); let snapshot_parent_inode = snapshot_entry.parent(); snapshot.reparent_entry(snapshot_inode, None, snapshot_parent_inode, None); possible_removed_inodes.insert(snapshot_inode); @@ -1167,6 +1202,9 @@ mod tests { let operations = env::var("OPERATIONS") .map(|o| o.parse().unwrap()) .unwrap_or(40); + let initial_entries = env::var("INITIAL_ENTRIES") + .map(|o| o.parse().unwrap()) + .unwrap_or(20); let seeds = if let Ok(seed) = env::var("SEED").map(|s| s.parse().unwrap()) { seed..seed + 1 } else { @@ -1178,7 +1216,7 @@ mod tests { let mut rng = StdRng::seed_from_u64(seed); let root_dir = tempdir::TempDir::new(&format!("test-{}", seed)).unwrap(); - for _ in 0..20 { + for _ in 0..initial_entries { randomly_mutate_tree(root_dir.path(), 1.0, &mut rng).unwrap(); } log::info!("Generated initial tree"); @@ -1201,12 +1239,14 @@ mod tests { if !events.is_empty() && rng.gen_bool(0.4) { let len = rng.gen_range(0..=events.len()); let to_deliver = events.drain(0..len).collect::>(); + log::info!("Delivering events: {:#?}", to_deliver); scanner.process_events(to_deliver); } else { events.extend(randomly_mutate_tree(root_dir.path(), 0.6, &mut rng).unwrap()); mutations_len -= 1; } } + log::info!("Quiescing: {:#?}", events); scanner.process_events(events); let (notify_tx, _notify_rx) = smol::channel::unbounded();