Track git status changes with the changed_paths system

This commit is contained in:
Mikayla Maki 2023-06-05 11:50:23 -07:00
parent ca077408d7
commit e56fcd69b5
No known key found for this signature in database
2 changed files with 83 additions and 45 deletions

View file

@ -1974,7 +1974,8 @@ impl LocalSnapshot {
entry entry
} }
fn build_repo(&mut self, parent_path: Arc<Path>, fs: &dyn Fs) -> Option<()> { #[must_use = "Changed paths must be used for diffing later"]
fn build_repo(&mut self, parent_path: Arc<Path>, fs: &dyn Fs) -> Option<Vec<Arc<Path>>> {
let abs_path = self.abs_path.join(&parent_path); let abs_path = self.abs_path.join(&parent_path);
let work_dir: Arc<Path> = parent_path.parent().unwrap().into(); let work_dir: Arc<Path> = parent_path.parent().unwrap().into();
@ -1991,7 +1992,10 @@ impl LocalSnapshot {
.entry_for_path(work_dir.clone()) .entry_for_path(work_dir.clone())
.map(|entry| entry.id)?; .map(|entry| entry.id)?;
if self.git_repositories.get(&work_dir_id).is_none() { if self.git_repositories.get(&work_dir_id).is_some() {
return None;
}
let repo = fs.open_repo(abs_path.as_path())?; let repo = fs.open_repo(abs_path.as_path())?;
let work_directory = RepositoryWorkDirectory(work_dir.clone()); let work_directory = RepositoryWorkDirectory(work_dir.clone());
@ -2005,7 +2009,7 @@ impl LocalSnapshot {
}, },
); );
self.scan_statuses(repo_lock.deref(), &work_directory); let changed_paths = self.scan_statuses(repo_lock.deref(), &work_directory);
drop(repo_lock); drop(repo_lock);
@ -2016,17 +2020,18 @@ impl LocalSnapshot {
repo_ptr: repo, repo_ptr: repo,
git_dir_path: parent_path.clone(), git_dir_path: parent_path.clone(),
}, },
) );
}
Some(changed_paths)
Some(())
} }
#[must_use = "Changed paths must be used for diffing later"]
fn scan_statuses( fn scan_statuses(
&mut self, &mut self,
repo_ptr: &dyn GitRepository, repo_ptr: &dyn GitRepository,
work_directory: &RepositoryWorkDirectory, work_directory: &RepositoryWorkDirectory,
) { ) -> Vec<Arc<Path>> {
let mut changes = vec![];
let mut edits = vec![]; let mut edits = vec![];
for mut entry in self for mut entry in self
.descendent_entries(false, false, &work_directory.0) .descendent_entries(false, false, &work_directory.0)
@ -2038,10 +2043,12 @@ impl LocalSnapshot {
let git_file_status = repo_ptr.status(&RepoPath(repo_path.into())); let git_file_status = repo_ptr.status(&RepoPath(repo_path.into()));
let status = git_file_status; let status = git_file_status;
entry.git_status = status; entry.git_status = status;
changes.push(entry.path.clone());
edits.push(Edit::Insert(entry)); edits.push(Edit::Insert(entry));
} }
self.entries_by_path.edit(edits, &()); self.entries_by_path.edit(edits, &());
changes
} }
fn ancestor_inodes_for_path(&self, path: &Path) -> TreeSet<u64> { fn ancestor_inodes_for_path(&self, path: &Path) -> TreeSet<u64> {
@ -2096,13 +2103,14 @@ impl BackgroundScannerState {
self.snapshot.insert_entry(entry, fs) self.snapshot.insert_entry(entry, fs)
} }
#[must_use = "Changed paths must be used for diffing later"]
fn populate_dir( fn populate_dir(
&mut self, &mut self,
parent_path: Arc<Path>, parent_path: Arc<Path>,
entries: impl IntoIterator<Item = Entry>, entries: impl IntoIterator<Item = Entry>,
ignore: Option<Arc<Gitignore>>, ignore: Option<Arc<Gitignore>>,
fs: &dyn Fs, fs: &dyn Fs,
) { ) -> Option<Vec<Arc<Path>>> {
let mut parent_entry = if let Some(parent_entry) = self let mut parent_entry = if let Some(parent_entry) = self
.snapshot .snapshot
.entries_by_path .entries_by_path
@ -2114,7 +2122,7 @@ impl BackgroundScannerState {
"populating a directory {:?} that has been removed", "populating a directory {:?} that has been removed",
parent_path parent_path
); );
return; return None;
}; };
match parent_entry.kind { match parent_entry.kind {
@ -2122,7 +2130,7 @@ impl BackgroundScannerState {
parent_entry.kind = EntryKind::Dir; parent_entry.kind = EntryKind::Dir;
} }
EntryKind::Dir => {} EntryKind::Dir => {}
_ => return, _ => return None,
} }
if let Some(ignore) = ignore { if let Some(ignore) = ignore {
@ -2152,8 +2160,9 @@ impl BackgroundScannerState {
self.snapshot.entries_by_id.edit(entries_by_id_edits, &()); self.snapshot.entries_by_id.edit(entries_by_id_edits, &());
if parent_path.file_name() == Some(&DOT_GIT) { if parent_path.file_name() == Some(&DOT_GIT) {
self.snapshot.build_repo(parent_path, fs); return self.snapshot.build_repo(parent_path, fs);
} }
None
} }
fn remove_path(&mut self, path: &Path) { fn remove_path(&mut self, path: &Path) {
@ -2822,14 +2831,16 @@ impl BackgroundScanner {
self.update_ignore_statuses().await; self.update_ignore_statuses().await;
{ {
let mut snapshot = &mut self.state.lock().snapshot; let mut state = self.state.lock();
if let Some(paths) = paths { if let Some(paths) = paths {
for path in paths { for path in paths {
self.reload_git_repo(&path, &mut *snapshot, self.fs.as_ref()); self.reload_git_repo(&path, &mut *state, self.fs.as_ref());
} }
} }
let mut snapshot = &mut state.snapshot;
let mut git_repositories = mem::take(&mut snapshot.git_repositories); let mut git_repositories = mem::take(&mut snapshot.git_repositories);
git_repositories.retain(|work_directory_id, _| { git_repositories.retain(|work_directory_id, _| {
snapshot snapshot
@ -3071,10 +3082,19 @@ impl BackgroundScanner {
{ {
let mut state = self.state.lock(); let mut state = self.state.lock();
let changed_paths =
state.populate_dir(job.path.clone(), new_entries, new_ignore, self.fs.as_ref()); state.populate_dir(job.path.clone(), new_entries, new_ignore, self.fs.as_ref());
if let Err(ix) = state.changed_paths.binary_search(&job.path) { if let Err(ix) = state.changed_paths.binary_search(&job.path) {
state.changed_paths.insert(ix, job.path.clone()); state.changed_paths.insert(ix, job.path.clone());
} }
if let Some(changed_paths) = changed_paths {
util::extend_sorted(
&mut state.changed_paths,
changed_paths,
usize::MAX,
Ord::cmp,
)
}
} }
for new_job in new_jobs { for new_job in new_jobs {
@ -3221,22 +3241,31 @@ impl BackgroundScanner {
fn reload_git_repo( fn reload_git_repo(
&self, &self,
path: &Path, path: &Path,
snapshot: &mut LocalSnapshot, state: &mut BackgroundScannerState,
fs: &dyn Fs, fs: &dyn Fs,
) -> Option<()> { ) -> Option<()> {
let scan_id = snapshot.scan_id; let scan_id = state.snapshot.scan_id;
if path if path
.components() .components()
.any(|component| component.as_os_str() == *DOT_GIT) .any(|component| component.as_os_str() == *DOT_GIT)
{ {
let (entry_id, repo_ptr) = { let (entry_id, repo_ptr) = {
let Some((entry_id, repo)) = snapshot.repo_for_metadata(&path) else { let Some((entry_id, repo)) = state.snapshot.repo_for_metadata(&path) else {
let dot_git_dir = path.ancestors() let dot_git_dir = path.ancestors()
.skip_while(|ancestor| ancestor.file_name() != Some(&*DOT_GIT)) .skip_while(|ancestor| ancestor.file_name() != Some(&*DOT_GIT))
.next()?; .next()?;
snapshot.build_repo(dot_git_dir.into(), fs); let changed_paths = state.snapshot.build_repo(dot_git_dir.into(), fs);
if let Some(changed_paths) = changed_paths {
util::extend_sorted(
&mut state.changed_paths,
changed_paths,
usize::MAX,
Ord::cmp,
);
}
return None; return None;
}; };
if repo.git_dir_scan_id == scan_id { if repo.git_dir_scan_id == scan_id {
@ -3246,7 +3275,8 @@ impl BackgroundScanner {
(*entry_id, repo.repo_ptr.to_owned()) (*entry_id, repo.repo_ptr.to_owned())
}; };
let work_dir = snapshot let work_dir = state
.snapshot
.entry_for_id(entry_id) .entry_for_id(entry_id)
.map(|entry| RepositoryWorkDirectory(entry.path.clone()))?; .map(|entry| RepositoryWorkDirectory(entry.path.clone()))?;
@ -3254,18 +3284,26 @@ impl BackgroundScanner {
repo.reload_index(); repo.reload_index();
let branch = repo.branch_name(); let branch = repo.branch_name();
snapshot.git_repositories.update(&entry_id, |entry| { state.snapshot.git_repositories.update(&entry_id, |entry| {
entry.git_dir_scan_id = scan_id; entry.git_dir_scan_id = scan_id;
}); });
snapshot state
.snapshot
.snapshot .snapshot
.repository_entries .repository_entries
.update(&work_dir, |entry| { .update(&work_dir, |entry| {
entry.branch = branch.map(Into::into); entry.branch = branch.map(Into::into);
}); });
snapshot.scan_statuses(repo.deref(), &work_dir); let changed_paths = state.snapshot.scan_statuses(repo.deref(), &work_dir);
util::extend_sorted(
&mut state.changed_paths,
changed_paths,
usize::MAX,
Ord::cmp,
)
} }
Some(()) Some(())

View file

@ -55,7 +55,7 @@ pub fn post_inc<T: From<u8> + AddAssign<T> + Copy>(value: &mut T) -> T {
} }
/// Extend a sorted vector with a sorted sequence of items, maintaining the vector's sort order and /// Extend a sorted vector with a sorted sequence of items, maintaining the vector's sort order and
/// enforcing a maximum length. Sort the items according to the given callback. Before calling this, /// enforcing a maximum length. This also de-duplicates items. Sort the items according to the given callback. Before calling this,
/// both `vec` and `new_items` should already be sorted according to the `cmp` comparator. /// both `vec` and `new_items` should already be sorted according to the `cmp` comparator.
pub fn extend_sorted<T, I, F>(vec: &mut Vec<T>, new_items: I, limit: usize, mut cmp: F) pub fn extend_sorted<T, I, F>(vec: &mut Vec<T>, new_items: I, limit: usize, mut cmp: F)
where where