Root rename detection (#20313)

Closes #5349

Release Notes:

- Fixed Zed when the directory that you opened is renamed.
This commit is contained in:
Conrad Irwin 2024-11-06 20:36:59 -07:00 committed by GitHub
parent 216ea4ddc4
commit e645aa9d20
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 196 additions and 4 deletions

View file

@ -285,6 +285,9 @@ pub struct LocalSnapshot {
/// All of the git repositories in the worktree, indexed by the project entry
/// id of their parent directory.
git_repositories: TreeMap<ProjectEntryId, LocalRepositoryEntry>,
/// The file handle of the root dir
/// (so we can find it after it's been moved)
root_file_handle: Option<Arc<dyn fs::FileHandle>>,
}
struct BackgroundScannerState {
@ -341,6 +344,9 @@ enum ScanState {
barrier: SmallVec<[barrier::Sender; 1]>,
scanning: bool,
},
RootUpdated {
new_path: Option<Arc<Path>>,
},
}
struct UpdateObservationState {
@ -382,6 +388,8 @@ impl Worktree {
true
});
let root_file_handle = fs.open_handle(&abs_path).await.log_err();
cx.new_model(move |cx: &mut ModelContext<Worktree>| {
let mut snapshot = LocalSnapshot {
ignores_by_parent_abs_path: Default::default(),
@ -393,6 +401,7 @@ impl Worktree {
.map_or(String::new(), |f| f.to_string_lossy().to_string()),
abs_path,
),
root_file_handle,
};
if let Some(metadata) = metadata {
@ -1076,6 +1085,17 @@ impl LocalWorktree {
this.set_snapshot(snapshot, changes, cx);
drop(barrier);
}
ScanState::RootUpdated { new_path } => {
if let Some(new_path) = new_path {
this.snapshot.git_repositories = Default::default();
this.snapshot.ignores_by_parent_abs_path = Default::default();
let root_name = new_path
.file_name()
.map_or(String::new(), |f| f.to_string_lossy().to_string());
this.snapshot.update_abs_path(new_path, root_name);
}
this.restart_background_scanners(cx);
}
}
cx.notify();
})
@ -2073,12 +2093,24 @@ impl Snapshot {
.and_then(|entry| entry.git_status)
}
fn update_abs_path(&mut self, abs_path: Arc<Path>, root_name: String) {
self.abs_path = abs_path;
if root_name != self.root_name {
self.root_char_bag = root_name.chars().map(|c| c.to_ascii_lowercase()).collect();
self.root_name = root_name;
}
}
pub(crate) fn apply_remote_update(&mut self, mut update: proto::UpdateWorktree) -> Result<()> {
log::trace!(
"applying remote worktree update. {} entries updated, {} removed",
update.updated_entries.len(),
update.removed_entries.len()
);
self.update_abs_path(
Arc::from(PathBuf::from(update.abs_path).as_path()),
update.root_name,
);
let mut entries_by_path_edits = Vec::new();
let mut entries_by_id_edits = Vec::new();
@ -3732,7 +3764,29 @@ impl BackgroundScanner {
let root_canonical_path = match self.fs.canonicalize(&root_path).await {
Ok(path) => path,
Err(err) => {
log::error!("failed to canonicalize root path: {}", err);
let new_path = self
.state
.lock()
.snapshot
.root_file_handle
.clone()
.and_then(|handle| handle.current_path(&self.fs).log_err())
.filter(|new_path| **new_path != *root_path);
if let Some(new_path) = new_path.as_ref() {
log::info!(
"root renamed from {} to {}",
root_path.display(),
new_path.display()
)
} else {
log::warn!("root path could not be canonicalized: {}", err);
}
self.status_updates_tx
.unbounded_send(ScanState::RootUpdated {
new_path: new_path.map(|p| p.into()),
})
.ok();
return;
}
};