WIP: Add stateful status bubbling to worktree

This commit is contained in:
Mikayla Maki 2023-06-01 16:51:34 -07:00
parent 4717ce1da3
commit 5e43dcaab8
No known key found for this signature in database
2 changed files with 110 additions and 27 deletions

View File

@ -199,6 +199,24 @@ pub enum GitFileStatus {
} }
impl GitFileStatus { impl GitFileStatus {
pub fn merge(
this: Option<GitFileStatus>,
other: Option<GitFileStatus>,
) -> Option<GitFileStatus> {
match (this, other) {
(Some(GitFileStatus::Conflict), _) | (_, Some(GitFileStatus::Conflict)) => {
Some(GitFileStatus::Conflict)
}
(Some(GitFileStatus::Modified), _) | (_, Some(GitFileStatus::Modified)) => {
Some(GitFileStatus::Modified)
}
(Some(GitFileStatus::Added), _) | (_, Some(GitFileStatus::Added)) => {
Some(GitFileStatus::Added)
}
_ => None,
}
}
pub fn from_proto(git_status: Option<i32>) -> Option<GitFileStatus> { pub fn from_proto(git_status: Option<i32>) -> Option<GitFileStatus> {
git_status.and_then(|status| { git_status.and_then(|status| {
proto::GitStatus::from_i32(status).map(|status| match status { proto::GitStatus::from_i32(status).map(|status| match status {

View File

@ -1958,20 +1958,32 @@ impl LocalSnapshot {
Some(()) Some(())
} }
fn scan_statuses(&mut self, repo_ptr: &dyn GitRepository, work_directory: &RepositoryWorkDirectory) { fn scan_statuses(
&mut self,
repo_ptr: &dyn GitRepository,
work_directory: &RepositoryWorkDirectory,
) {
let statuses = repo_ptr.statuses().unwrap_or_default(); let statuses = repo_ptr.statuses().unwrap_or_default();
let mut edits = vec![];
for (repo_path, status) in statuses.iter() { for (repo_path, status) in statuses.iter() {
let Some(entry) = self.entry_for_path(&work_directory.0.join(repo_path)) else { self.set_git_status(&work_directory.0.join(repo_path), Some(*status), &mut edits);
}
self.entries_by_path.edit(edits, &());
}
fn set_git_status(
&self,
path: &Path,
status: Option<GitFileStatus>,
edits: &mut Vec<Edit<Entry>>,
) {
for path in path.ancestors() {
let Some(entry) = self.entry_for_path(path) else {
continue; continue;
}; };
let mut entry = entry.clone(); let mut entry = entry.clone();
entry.git_status = Some(*status); entry.git_status = GitFileStatus::merge(entry.git_status, status);
edits.push(Edit::Insert(entry))
// TODO statuses
// Bubble
self.entries_by_path.insert_or_replace(entry, &());
} }
} }
@ -3161,7 +3173,6 @@ impl BackgroundScanner {
entry.branch = branch.map(Into::into); entry.branch = branch.map(Into::into);
}); });
snapshot.scan_statuses(repo.deref(), &work_dir); snapshot.scan_statuses(repo.deref(), &work_dir);
} else { } else {
if snapshot if snapshot
@ -3185,13 +3196,13 @@ impl BackgroundScanner {
return None; return None;
} }
for mut entry in snapshot let mut edits = vec![];
for path in snapshot
.descendent_entries(false, false, path) .descendent_entries(false, false, path)
.cloned() .map(|entry| &entry.path)
.collect::<Vec<_>>()
.into_iter()
{ {
let Some(repo_path) = repo.work_directory.relativize(snapshot, &entry.path) else { let Some(repo_path) = repo.work_directory.relativize(snapshot, &path) else {
continue; continue;
}; };
@ -3199,11 +3210,10 @@ impl BackgroundScanner {
continue; continue;
}; };
entry.git_status = Some(status); snapshot.set_git_status(&path, Some(status), &mut edits);
snapshot.entries_by_path.insert_or_replace(entry, &());
// TODO statuses
// Bubble
} }
snapshot.entries_by_path.edit(edits, &());
} }
Some(()) Some(())
@ -4995,7 +5005,6 @@ mod tests {
cx.read(|cx| tree.read(cx).as_local().unwrap().scan_complete()) cx.read(|cx| tree.read(cx).as_local().unwrap().scan_complete())
.await; .await;
const A_TXT: &'static str = "a.txt"; const A_TXT: &'static str = "a.txt";
const B_TXT: &'static str = "b.txt"; const B_TXT: &'static str = "b.txt";
const E_TXT: &'static str = "c/d/e.txt"; const E_TXT: &'static str = "c/d/e.txt";
@ -5012,8 +5021,6 @@ mod tests {
git_add(Path::new(DOTGITIGNORE), &repo); git_add(Path::new(DOTGITIGNORE), &repo);
git_commit("Initial commit", &repo); git_commit("Initial commit", &repo);
std::fs::write(work_dir.join(A_TXT), "aa").unwrap();
tree.flush_fs_events(cx).await; tree.flush_fs_events(cx).await;
deterministic.run_until_parked(); deterministic.run_until_parked();
@ -5024,10 +5031,6 @@ mod tests {
let (dir, _) = snapshot.repository_entries.iter().next().unwrap(); let (dir, _) = snapshot.repository_entries.iter().next().unwrap();
assert_eq!(dir.0.as_ref(), Path::new("project")); assert_eq!(dir.0.as_ref(), Path::new("project"));
assert_eq!(
snapshot.status_for_file(project_path.join(A_TXT)),
Some(GitFileStatus::Modified)
);
assert_eq!( assert_eq!(
snapshot.status_for_file(project_path.join(B_TXT)), snapshot.status_for_file(project_path.join(B_TXT)),
Some(GitFileStatus::Added) Some(GitFileStatus::Added)
@ -5036,6 +5039,36 @@ mod tests {
snapshot.status_for_file(project_path.join(F_TXT)), snapshot.status_for_file(project_path.join(F_TXT)),
Some(GitFileStatus::Added) Some(GitFileStatus::Added)
); );
// Check stateful bubbling works
assert_eq!(
snapshot.status_for_file(project_path),
Some(GitFileStatus::Added)
);
assert_eq!(snapshot.status_for_file(""), Some(GitFileStatus::Added));
});
std::fs::write(work_dir.join(A_TXT), "aa").unwrap();
tree.flush_fs_events(cx).await;
deterministic.run_until_parked();
tree.read_with(cx, |tree, _cx| {
let snapshot = tree.snapshot();
assert_eq!(
snapshot.status_for_file(project_path.join(A_TXT)),
Some(GitFileStatus::Modified)
);
// Check stateful bubbling works, modified overrules added
assert_eq!(
snapshot.status_for_file(project_path),
Some(GitFileStatus::Modified)
);
assert_eq!(snapshot.status_for_file(""), Some(GitFileStatus::Modified));
}); });
git_add(Path::new(A_TXT), &repo); git_add(Path::new(A_TXT), &repo);
@ -5052,6 +5085,17 @@ mod tests {
snapshot.status_for_file(project_path.join(F_TXT)), snapshot.status_for_file(project_path.join(F_TXT)),
Some(GitFileStatus::Added) Some(GitFileStatus::Added)
); );
assert_eq!(snapshot.status_for_file(project_path.join(B_TXT)), None);
assert_eq!(snapshot.status_for_file(project_path.join(A_TXT)), None);
// Check bubbling
assert_eq!(
snapshot.status_for_file(project_path),
Some(GitFileStatus::Added)
);
assert_eq!(snapshot.status_for_file(""), Some(GitFileStatus::Added));
}); });
git_reset(0, &repo); git_reset(0, &repo);
@ -5075,6 +5119,23 @@ mod tests {
snapshot.status_for_file(project_path.join(E_TXT)), snapshot.status_for_file(project_path.join(E_TXT)),
Some(GitFileStatus::Modified) Some(GitFileStatus::Modified)
); );
// Check status bubbling
assert_eq!(
snapshot.status_for_file(project_path.join(Path::new(E_TXT).parent().unwrap())),
Some(GitFileStatus::Modified)
);
assert_eq!(
snapshot.status_for_file(
project_path.join(Path::new(E_TXT).parent().unwrap().parent().unwrap())
),
Some(GitFileStatus::Modified)
);
assert_eq!(
snapshot.status_for_file(project_path),
Some(GitFileStatus::Modified)
);
assert_eq!( assert_eq!(
snapshot.status_for_file(project_path.join(F_TXT)), snapshot.status_for_file(project_path.join(F_TXT)),
Some(GitFileStatus::Added) Some(GitFileStatus::Added)
@ -5132,7 +5193,11 @@ mod tests {
let snapshot = tree.snapshot(); let snapshot = tree.snapshot();
assert_eq!( assert_eq!(
snapshot.status_for_file(Path::new(renamed_dir_name).join(RENAMED_FILE)), snapshot.status_for_file(
project_path
.join(Path::new(renamed_dir_name))
.join(RENAMED_FILE)
),
Some(GitFileStatus::Added) Some(GitFileStatus::Added)
); );
}); });