WIP: Track live entry status in repository

co-authored-by: petros <petros@zed.dev>
This commit is contained in:
Mikayla Maki 2023-05-09 10:02:58 -07:00 committed by Mikayla Maki
parent 7169f5c760
commit 67491632cb
No known key found for this signature in database
3 changed files with 165 additions and 86 deletions

View file

@ -6,7 +6,10 @@ use anyhow::{anyhow, Context, Result};
use client::{proto, Client};
use clock::ReplicaId;
use collections::{HashMap, VecDeque};
use fs::{repository::{GitRepository, RepoPath, GitStatus}, Fs, LineEnding};
use fs::{
repository::{GitRepository, GitStatus, RepoPath},
Fs, LineEnding,
};
use futures::{
channel::{
mpsc::{self, UnboundedSender},
@ -121,7 +124,7 @@ pub struct Snapshot {
pub struct RepositoryEntry {
pub(crate) work_directory: WorkDirectoryEntry,
pub(crate) branch: Option<Arc<str>>,
// pub(crate) statuses: TreeMap<RepoPath, GitStatus>
pub(crate) statuses: TreeMap<RepoPath, GitStatus>,
}
impl RepositoryEntry {
@ -169,7 +172,6 @@ impl AsRef<Path> for RepositoryWorkDirectory {
}
}
#[derive(Clone, Debug, Ord, PartialOrd, Eq, PartialEq)]
pub struct WorkDirectoryEntry(ProjectEntryId);
@ -219,6 +221,7 @@ pub struct LocalSnapshot {
#[derive(Debug, Clone)]
pub struct LocalRepositoryEntry {
pub(crate) scan_id: usize,
pub(crate) full_scan_id: usize,
pub(crate) repo_ptr: Arc<Mutex<dyn GitRepository>>,
/// Path to the actual .git folder.
/// Note: if .git is a file, this points to the folder indicated by the .git file
@ -1412,6 +1415,8 @@ impl Snapshot {
let repository = RepositoryEntry {
work_directory: ProjectEntryId::from_proto(repository.work_directory_id).into(),
branch: repository.branch.map(Into::into),
// TODO: status
statuses: Default::default(),
};
if let Some(entry) = self.entry_for_id(repository.work_directory_id()) {
self.repository_entries
@ -1572,6 +1577,10 @@ impl LocalSnapshot {
current_candidate.map(|entry| entry.to_owned())
}
pub(crate) fn get_local_repo(&self, repo: &RepositoryEntry) -> Option<&LocalRepositoryEntry> {
self.git_repositories.get(&repo.work_directory.0)
}
pub(crate) fn repo_for_metadata(
&self,
path: &Path,
@ -1842,6 +1851,7 @@ impl LocalSnapshot {
RepositoryEntry {
work_directory: work_dir_id.into(),
branch: repo_lock.branch_name().map(Into::into),
statuses: repo_lock.statuses().unwrap_or_default(),
},
);
drop(repo_lock);
@ -1850,6 +1860,7 @@ impl LocalSnapshot {
work_dir_id,
LocalRepositoryEntry {
scan_id,
full_scan_id: scan_id,
repo_ptr: repo,
git_dir_path: parent_path.clone(),
},
@ -2825,26 +2836,7 @@ impl BackgroundScanner {
fs_entry.is_ignored = ignore_stack.is_all();
snapshot.insert_entry(fs_entry, self.fs.as_ref());
let scan_id = snapshot.scan_id;
let repo_with_path_in_dotgit = snapshot.repo_for_metadata(&path);
if let Some((entry_id, repo)) = repo_with_path_in_dotgit {
let work_dir = snapshot
.entry_for_id(entry_id)
.map(|entry| RepositoryWorkDirectory(entry.path.clone()))?;
let repo = repo.lock();
repo.reload_index();
let branch = repo.branch_name();
snapshot.git_repositories.update(&entry_id, |entry| {
entry.scan_id = scan_id;
});
snapshot
.repository_entries
.update(&work_dir, |entry| entry.branch = branch.map(Into::into));
}
self.reload_repo_for_path(&path, &mut snapshot);
if let Some(scan_queue_tx) = &scan_queue_tx {
let mut ancestor_inodes = snapshot.ancestor_inodes_for_path(&path);
@ -2872,6 +2864,63 @@ impl BackgroundScanner {
Some(event_paths)
}
fn reload_repo_for_path(&self, path: &Path, snapshot: &mut LocalSnapshot) -> Option<()> {
let scan_id = snapshot.scan_id;
if path
.components()
.any(|component| component.as_os_str() == *DOT_GIT)
{
let (entry_id, repo) = snapshot.repo_for_metadata(&path)?;
let work_dir = snapshot
.entry_for_id(entry_id)
.map(|entry| RepositoryWorkDirectory(entry.path.clone()))?;
let repo = repo.lock();
repo.reload_index();
let branch = repo.branch_name();
let statuses = repo.statuses().unwrap_or_default();
snapshot.git_repositories.update(&entry_id, |entry| {
entry.scan_id = scan_id;
entry.full_scan_id = scan_id;
});
snapshot.repository_entries.update(&work_dir, |entry| {
entry.branch = branch.map(Into::into);
entry.statuses = statuses;
});
} else if let Some(repo) = snapshot.repo_for(&path) {
let status = {
let local_repo = snapshot.get_local_repo(&repo)?;
// Short circuit if we've already scanned everything
if local_repo.full_scan_id == scan_id {
return None;
}
let repo_path = repo.work_directory.relativize(&snapshot, &path)?;
let git_ptr = local_repo.repo_ptr.lock();
git_ptr.file_status(&repo_path)?
};
if status != GitStatus::Untracked {
let work_dir = repo.work_directory(snapshot)?;
let work_dir_id = repo.work_directory;
snapshot
.git_repositories
.update(&work_dir_id, |entry| entry.scan_id = scan_id);
snapshot
.repository_entries
.update(&work_dir, |entry| entry.statuses.insert(repo_path, status));
}
}
Some(())
}
async fn update_ignore_statuses(&self) {
use futures::FutureExt as _;