Compare commits

...

3 Commits

Author SHA1 Message Date
Piotr Osiewicz
b23f0b286d Remove dbg statement 2024-04-30 14:00:41 +02:00
Piotr Osiewicz
5118fea863 Use unstaged_statuses in more places 2024-04-30 13:52:46 +02:00
Piotr Osiewicz
97a3a32e08 WIP: Batch unstaged file requests for git 2024-04-30 12:50:58 +02:00
2 changed files with 64 additions and 16 deletions

View File

@@ -42,6 +42,7 @@ pub trait GitRepository: Send {
/// the index stores hashes of trees, so that unchanged directories can be skipped.
fn staged_statuses(&self, path_prefix: &Path) -> TreeMap<RepoPath, GitFileStatus>;
fn unstaged_statuses(&self, path_prefix: &Path) -> TreeMap<RepoPath, GitFileStatus>;
/// Get the status of a given file in the working directory with respect to
/// the index. In the common case, when there are no changes, this only requires
/// an index lookup. The index stores the mtime of each file when it was added,
@@ -150,6 +151,29 @@ impl GitRepository for RealGitRepository {
map
}
fn unstaged_statuses(&self, path_prefix: &Path) -> TreeMap<RepoPath, GitFileStatus> {
let mut map = TreeMap::default();
let mut options = git2::StatusOptions::new();
options.include_untracked(true);
options.recurse_untracked_dirs(true);
options.pathspec(path_prefix);
options.show(StatusShow::Workdir);
if let Some(statuses) = self.repository.statuses(Some(&mut options)).log_err() {
for status in statuses.iter() {
let path = RepoPath(PathBuf::try_from_bytes(status.path_bytes()).unwrap());
let status = status.status();
if !status.contains(git2::Status::IGNORED) {
if let Some(status) = read_status(status) {
map.insert(path, status)
}
}
}
}
map
}
fn unstaged_status(&self, path: &RepoPath, mtime: SystemTime) -> Option<GitFileStatus> {
// If the file has not changed since it was added to the index, then
// there can't be any changes.
@@ -333,6 +357,16 @@ impl GitRepository for FakeGitRepository {
map
}
fn unstaged_statuses(&self, path_prefix: &Path) -> TreeMap<RepoPath, GitFileStatus> {
let mut map = TreeMap::default();
let state = self.state.lock();
for (repo_path, status) in state.worktree_statuses.iter() {
if repo_path.0.starts_with(path_prefix) {
map.insert(repo_path.to_owned(), status.to_owned());
}
}
map
}
fn unstaged_status(&self, _path: &RepoPath, _mtime: SystemTime) -> Option<GitFileStatus> {
None
}

View File

@@ -2523,10 +2523,14 @@ impl BackgroundScannerState {
if !ignore_stack.is_abs_path_ignored(&abs_path, true) {
if let Some((workdir_path, repo)) = self.snapshot.local_repo_for_path(&path) {
if let Ok(repo_path) = path.strip_prefix(&workdir_path.0) {
let repo_ptr = repo.repo_ptr.lock();
let staged_statuses = repo_ptr.staged_statuses(repo_path);
let unstaged_statuses = repo_ptr.unstaged_statuses(repo_path);
containing_repository = Some((
workdir_path,
repo.repo_ptr.clone(),
repo.repo_ptr.lock().staged_statuses(repo_path),
staged_statuses,
unstaged_statuses,
));
}
}
@@ -2749,7 +2753,8 @@ impl BackgroundScannerState {
) -> Option<(
RepositoryWorkDirectory,
Arc<Mutex<dyn GitRepository>>,
TreeMap<RepoPath, GitFileStatus>,
StagedStatuses,
UnstagedStatuses,
)> {
let work_dir_path: Arc<Path> = match dot_git_path.parent() {
Some(parent_dir) => {
@@ -2797,7 +2802,8 @@ impl BackgroundScannerState {
},
);
let staged_statuses = self.update_git_statuses(&work_directory, &*repo_lock);
let (staged_statuses, unstaged_statuses) =
self.update_git_statuses(&work_directory, &*repo_lock);
drop(repo_lock);
self.snapshot.git_repositories.insert(
@@ -2809,36 +2815,40 @@ impl BackgroundScannerState {
},
);
Some((work_directory, repository, staged_statuses))
Some((
work_directory,
repository,
staged_statuses,
unstaged_statuses,
))
}
fn update_git_statuses(
&mut self,
work_directory: &RepositoryWorkDirectory,
repo: &dyn GitRepository,
) -> TreeMap<RepoPath, GitFileStatus> {
) -> (StagedStatuses, UnstagedStatuses) {
let staged_statuses = repo.staged_statuses(Path::new(""));
let unstaged_statuses = repo.unstaged_statuses(Path::new(""));
let mut changes = vec![];
let mut edits = vec![];
for mut entry in self
for entry in self
.snapshot
.descendent_entries(false, false, &work_directory.0)
.cloned()
{
let Ok(repo_path) = entry.path.strip_prefix(&work_directory.0) else {
continue;
};
let Some(mtime) = entry.mtime else {
continue;
};
let repo_path = RepoPath(repo_path.to_path_buf());
let git_file_status = combine_git_statuses(
staged_statuses.get(&repo_path).copied(),
repo.unstaged_status(&repo_path, mtime),
unstaged_statuses.get(&repo_path).copied(),
);
if entry.git_status != git_file_status {
let mut entry = entry.clone();
entry.git_status = git_file_status;
changes.push(entry.path.clone());
edits.push(Edit::Insert(entry));
@@ -2847,7 +2857,7 @@ impl BackgroundScannerState {
self.snapshot.entries_by_path.edit(edits, &());
util::extend_sorted(&mut self.changed_paths, changes, usize::MAX, Ord::cmp);
staged_statuses
(staged_statuses, unstaged_statuses)
}
}
@@ -3976,15 +3986,15 @@ impl BackgroundScanner {
} else {
child_entry.is_ignored = ignore_stack.is_abs_path_ignored(&child_abs_path, false);
if !child_entry.is_ignored {
if let Some((repository_dir, repository, staged_statuses)) =
if let Some((repository_dir, _, staged_statuses, unstaged_statuses)) =
&job.containing_repository
{
if let Ok(repo_path) = child_entry.path.strip_prefix(&repository_dir.0) {
if let Some(mtime) = child_entry.mtime {
if child_entry.mtime.is_some() {
let repo_path = RepoPath(repo_path.into());
child_entry.git_status = combine_git_statuses(
staged_statuses.get(&repo_path).copied(),
repository.lock().unstaged_status(&repo_path, mtime),
unstaged_statuses.get(&repo_path).copied(),
);
}
}
@@ -4481,6 +4491,9 @@ fn char_bag_for_path(root_char_bag: CharBag, path: &Path) -> CharBag {
result
}
type StagedStatuses = TreeMap<RepoPath, GitFileStatus>;
type UnstagedStatuses = TreeMap<RepoPath, GitFileStatus>;
struct ScanJob {
abs_path: Arc<Path>,
path: Arc<Path>,
@@ -4491,7 +4504,8 @@ struct ScanJob {
containing_repository: Option<(
RepositoryWorkDirectory,
Arc<Mutex<dyn GitRepository>>,
TreeMap<RepoPath, GitFileStatus>,
StagedStatuses,
UnstagedStatuses,
)>,
}