Compute unstaged git status separately, to take advantage of our cached file mtimes

This commit is contained in:
Max Brunsfeld 2023-07-21 17:51:00 -07:00
parent ff0864026e
commit 51d311affd
2 changed files with 92 additions and 25 deletions

View file

@ -10,6 +10,7 @@ use std::{
os::unix::prelude::OsStrExt,
path::{Component, Path, PathBuf},
sync::Arc,
time::SystemTime,
};
use sum_tree::{MapSeekTarget, TreeMap};
use util::ResultExt;
@ -27,7 +28,8 @@ pub trait GitRepository: Send {
fn reload_index(&self);
fn load_index_text(&self, relative_file_path: &Path) -> Option<String>;
fn branch_name(&self) -> Option<String>;
fn statuses(&self, path_prefix: &Path) -> TreeMap<RepoPath, GitFileStatus>;
fn staged_statuses(&self, path_prefix: &Path) -> TreeMap<RepoPath, GitFileStatus>;
fn unstaged_status(&self, path: &RepoPath, mtime: SystemTime) -> Option<GitFileStatus>;
fn status(&self, path: &RepoPath) -> Result<Option<GitFileStatus>>;
fn branches(&self) -> Result<Vec<Branch>>;
fn change_branch(&self, _: &str) -> Result<()>;
@ -78,10 +80,11 @@ impl GitRepository for LibGitRepository {
Some(branch.to_string())
}
fn statuses(&self, path_prefix: &Path) -> TreeMap<RepoPath, GitFileStatus> {
fn staged_statuses(&self, path_prefix: &Path) -> TreeMap<RepoPath, GitFileStatus> {
let mut map = TreeMap::default();
let mut options = git2::StatusOptions::new();
options.pathspec(path_prefix);
options.disable_pathspec_match(true);
if let Some(statuses) = self.statuses(Some(&mut options)).log_err() {
for status in statuses
.iter()
@ -98,6 +101,27 @@ impl GitRepository for LibGitRepository {
map
}
fn unstaged_status(&self, path: &RepoPath, mtime: SystemTime) -> Option<GitFileStatus> {
let index = self.index().log_err()?;
if let Some(entry) = index.get_path(&path, 0) {
let mtime = mtime.duration_since(SystemTime::UNIX_EPOCH).log_err()?;
if entry.mtime.seconds() == mtime.as_secs() as i32
&& entry.mtime.nanoseconds() == mtime.subsec_nanos()
{
None
} else {
let mut options = git2::StatusOptions::new();
options.pathspec(&path.0);
options.disable_pathspec_match(true);
let statuses = self.statuses(Some(&mut options)).log_err()?;
let status = statuses.get(0).and_then(|s| read_status(s.status()));
status
}
} else {
Some(GitFileStatus::Added)
}
}
fn status(&self, path: &RepoPath) -> Result<Option<GitFileStatus>> {
let status = self.status_file(path);
match status {
@ -203,7 +227,7 @@ impl GitRepository for FakeGitRepository {
state.branch_name.clone()
}
fn statuses(&self, path_prefix: &Path) -> TreeMap<RepoPath, GitFileStatus> {
fn staged_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() {
@ -214,6 +238,10 @@ impl GitRepository for FakeGitRepository {
map
}
fn unstaged_status(&self, _path: &RepoPath, _mtime: SystemTime) -> Option<GitFileStatus> {
None
}
fn status(&self, path: &RepoPath) -> Result<Option<GitFileStatus>> {
let state = self.state.lock();
Ok(state.worktree_statuses.get(path).cloned())