Fix bug in status detection when removing a directory
This commit is contained in:
parent
0082d68d4a
commit
23a19d85b8
5 changed files with 108 additions and 105 deletions
|
@ -10,7 +10,7 @@ use editor::{
|
||||||
ConfirmRename, Editor, ExcerptRange, MultiBuffer, Redo, Rename, ToOffset, ToggleCodeActions,
|
ConfirmRename, Editor, ExcerptRange, MultiBuffer, Redo, Rename, ToOffset, ToggleCodeActions,
|
||||||
Undo,
|
Undo,
|
||||||
};
|
};
|
||||||
use fs::{repository::GitStatus, FakeFs, Fs as _, LineEnding, RemoveOptions};
|
use fs::{repository::GitFileStatus, FakeFs, Fs as _, LineEnding, RemoveOptions};
|
||||||
use futures::StreamExt as _;
|
use futures::StreamExt as _;
|
||||||
use gpui::{
|
use gpui::{
|
||||||
executor::Deterministic, geometry::vector::vec2f, test::EmptyView, AppContext, ModelHandle,
|
executor::Deterministic, geometry::vector::vec2f, test::EmptyView, AppContext, ModelHandle,
|
||||||
|
@ -2728,8 +2728,8 @@ async fn test_git_status_sync(
|
||||||
.set_status_for_repo(
|
.set_status_for_repo(
|
||||||
Path::new("/dir/.git"),
|
Path::new("/dir/.git"),
|
||||||
&[
|
&[
|
||||||
(&Path::new(A_TXT), GitStatus::Added),
|
(&Path::new(A_TXT), GitFileStatus::Added),
|
||||||
(&Path::new(B_TXT), GitStatus::Added),
|
(&Path::new(B_TXT), GitFileStatus::Added),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
|
@ -2748,7 +2748,7 @@ async fn test_git_status_sync(
|
||||||
deterministic.run_until_parked();
|
deterministic.run_until_parked();
|
||||||
|
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
fn assert_status(file: &impl AsRef<Path>, status: Option<GitStatus>, project: &Project, cx: &AppContext) {
|
fn assert_status(file: &impl AsRef<Path>, status: Option<GitFileStatus>, project: &Project, cx: &AppContext) {
|
||||||
let file = file.as_ref();
|
let file = file.as_ref();
|
||||||
let worktrees = project.visible_worktrees(cx).collect::<Vec<_>>();
|
let worktrees = project.visible_worktrees(cx).collect::<Vec<_>>();
|
||||||
assert_eq!(worktrees.len(), 1);
|
assert_eq!(worktrees.len(), 1);
|
||||||
|
@ -2760,12 +2760,12 @@ async fn test_git_status_sync(
|
||||||
|
|
||||||
// Smoke test status reading
|
// Smoke test status reading
|
||||||
project_local.read_with(cx_a, |project, cx| {
|
project_local.read_with(cx_a, |project, cx| {
|
||||||
assert_status(&Path::new(A_TXT), Some(GitStatus::Added), project, cx);
|
assert_status(&Path::new(A_TXT), Some(GitFileStatus::Added), project, cx);
|
||||||
assert_status(&Path::new(B_TXT), Some(GitStatus::Added), project, cx);
|
assert_status(&Path::new(B_TXT), Some(GitFileStatus::Added), project, cx);
|
||||||
});
|
});
|
||||||
project_remote.read_with(cx_b, |project, cx| {
|
project_remote.read_with(cx_b, |project, cx| {
|
||||||
assert_status(&Path::new(A_TXT), Some(GitStatus::Added), project, cx);
|
assert_status(&Path::new(A_TXT), Some(GitFileStatus::Added), project, cx);
|
||||||
assert_status(&Path::new(B_TXT), Some(GitStatus::Added), project, cx);
|
assert_status(&Path::new(B_TXT), Some(GitFileStatus::Added), project, cx);
|
||||||
});
|
});
|
||||||
|
|
||||||
client_a
|
client_a
|
||||||
|
@ -2774,8 +2774,8 @@ async fn test_git_status_sync(
|
||||||
.set_status_for_repo(
|
.set_status_for_repo(
|
||||||
Path::new("/dir/.git"),
|
Path::new("/dir/.git"),
|
||||||
&[
|
&[
|
||||||
(&Path::new(A_TXT), GitStatus::Modified),
|
(&Path::new(A_TXT), GitFileStatus::Modified),
|
||||||
(&Path::new(B_TXT), GitStatus::Modified),
|
(&Path::new(B_TXT), GitFileStatus::Modified),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
|
@ -2785,19 +2785,19 @@ async fn test_git_status_sync(
|
||||||
|
|
||||||
// Smoke test status reading
|
// Smoke test status reading
|
||||||
project_local.read_with(cx_a, |project, cx| {
|
project_local.read_with(cx_a, |project, cx| {
|
||||||
assert_status(&Path::new(A_TXT), Some(GitStatus::Added), project, cx);
|
assert_status(&Path::new(A_TXT), Some(GitFileStatus::Added), project, cx);
|
||||||
assert_status(&Path::new(B_TXT), Some(GitStatus::Added), project, cx);
|
assert_status(&Path::new(B_TXT), Some(GitFileStatus::Added), project, cx);
|
||||||
});
|
});
|
||||||
project_remote.read_with(cx_b, |project, cx| {
|
project_remote.read_with(cx_b, |project, cx| {
|
||||||
assert_status(&Path::new(A_TXT), Some(GitStatus::Added), project, cx);
|
assert_status(&Path::new(A_TXT), Some(GitFileStatus::Added), project, cx);
|
||||||
assert_status(&Path::new(B_TXT), Some(GitStatus::Added), project, cx);
|
assert_status(&Path::new(B_TXT), Some(GitFileStatus::Added), project, cx);
|
||||||
});
|
});
|
||||||
|
|
||||||
// And synchronization while joining
|
// And synchronization while joining
|
||||||
let project_remote_c = client_c.build_remote_project(project_id, cx_c).await;
|
let project_remote_c = client_c.build_remote_project(project_id, cx_c).await;
|
||||||
project_remote_c.read_with(cx_c, |project, cx| {
|
project_remote_c.read_with(cx_c, |project, cx| {
|
||||||
assert_status(&Path::new(A_TXT), Some(GitStatus::Added), project, cx);
|
assert_status(&Path::new(A_TXT), Some(GitFileStatus::Added), project, cx);
|
||||||
assert_status(&Path::new(B_TXT), Some(GitStatus::Added), project, cx);
|
assert_status(&Path::new(B_TXT), Some(GitFileStatus::Added), project, cx);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,7 @@ use git2::Repository as LibGitRepository;
|
||||||
use lazy_static::lazy_static;
|
use lazy_static::lazy_static;
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
use repository::{GitRepository, GitStatus};
|
use repository::{GitRepository, GitFileStatus};
|
||||||
use rope::Rope;
|
use rope::Rope;
|
||||||
use smol::io::{AsyncReadExt, AsyncWriteExt};
|
use smol::io::{AsyncReadExt, AsyncWriteExt};
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
|
@ -654,10 +654,10 @@ impl FakeFs {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn set_status_for_repo(&self, dot_git: &Path, statuses: &[(&Path, GitStatus)]) {
|
pub async fn set_status_for_repo(&self, dot_git: &Path, statuses: &[(&Path, GitFileStatus)]) {
|
||||||
self.with_git_state(dot_git, |state| {
|
self.with_git_state(dot_git, |state| {
|
||||||
state.git_statuses.clear();
|
state.worktree_statuses.clear();
|
||||||
state.git_statuses.extend(
|
state.worktree_statuses.extend(
|
||||||
statuses
|
statuses
|
||||||
.iter()
|
.iter()
|
||||||
.map(|(path, content)| {
|
.map(|(path, content)| {
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use collections::HashMap;
|
use collections::HashMap;
|
||||||
use git2::Status;
|
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
use std::{
|
use std::{
|
||||||
ffi::OsStr,
|
ffi::OsStr,
|
||||||
|
@ -21,9 +20,9 @@ pub trait GitRepository: Send {
|
||||||
|
|
||||||
fn branch_name(&self) -> Option<String>;
|
fn branch_name(&self) -> Option<String>;
|
||||||
|
|
||||||
fn statuses(&self) -> Option<TreeMap<RepoPath, GitStatus>>;
|
fn worktree_statuses(&self) -> Option<TreeMap<RepoPath, GitFileStatus>>;
|
||||||
|
|
||||||
fn file_status(&self, path: &RepoPath) -> Option<GitStatus>;
|
fn worktree_status(&self, path: &RepoPath) -> Option<GitFileStatus>;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::fmt::Debug for dyn GitRepository {
|
impl std::fmt::Debug for dyn GitRepository {
|
||||||
|
@ -70,7 +69,7 @@ impl GitRepository for LibGitRepository {
|
||||||
Some(branch.to_string())
|
Some(branch.to_string())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn statuses(&self) -> Option<TreeMap<RepoPath, GitStatus>> {
|
fn worktree_statuses(&self) -> Option<TreeMap<RepoPath, GitFileStatus>> {
|
||||||
let statuses = self.statuses(None).log_err()?;
|
let statuses = self.statuses(None).log_err()?;
|
||||||
|
|
||||||
let mut map = TreeMap::default();
|
let mut map = TreeMap::default();
|
||||||
|
@ -80,17 +79,31 @@ impl GitRepository for LibGitRepository {
|
||||||
.filter(|status| !status.status().contains(git2::Status::IGNORED))
|
.filter(|status| !status.status().contains(git2::Status::IGNORED))
|
||||||
{
|
{
|
||||||
let path = RepoPath(PathBuf::from(OsStr::from_bytes(status.path_bytes())));
|
let path = RepoPath(PathBuf::from(OsStr::from_bytes(status.path_bytes())));
|
||||||
|
let Some(status) = read_status(status.status()) else {
|
||||||
|
continue
|
||||||
|
};
|
||||||
|
|
||||||
map.insert(path, status.status().into())
|
map.insert(path, status)
|
||||||
}
|
}
|
||||||
|
|
||||||
Some(map)
|
Some(map)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn file_status(&self, path: &RepoPath) -> Option<GitStatus> {
|
fn worktree_status(&self, path: &RepoPath) -> Option<GitFileStatus> {
|
||||||
let status = self.status_file(path).log_err()?;
|
let status = self.status_file(path).log_err()?;
|
||||||
|
read_status(status)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Some(status.into())
|
fn read_status(status: git2::Status) -> Option<GitFileStatus> {
|
||||||
|
if status.contains(git2::Status::CONFLICTED) {
|
||||||
|
Some(GitFileStatus::Conflict)
|
||||||
|
} else if status.intersects(git2::Status::WT_MODIFIED | git2::Status::WT_RENAMED) {
|
||||||
|
Some(GitFileStatus::Modified)
|
||||||
|
} else if status.intersects(git2::Status::WT_NEW) {
|
||||||
|
Some(GitFileStatus::Added)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -102,7 +115,7 @@ pub struct FakeGitRepository {
|
||||||
#[derive(Debug, Clone, Default)]
|
#[derive(Debug, Clone, Default)]
|
||||||
pub struct FakeGitRepositoryState {
|
pub struct FakeGitRepositoryState {
|
||||||
pub index_contents: HashMap<PathBuf, String>,
|
pub index_contents: HashMap<PathBuf, String>,
|
||||||
pub git_statuses: HashMap<RepoPath, GitStatus>,
|
pub worktree_statuses: HashMap<RepoPath, GitFileStatus>,
|
||||||
pub branch_name: Option<String>,
|
pub branch_name: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -126,18 +139,18 @@ impl GitRepository for FakeGitRepository {
|
||||||
state.branch_name.clone()
|
state.branch_name.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn statuses(&self) -> Option<TreeMap<RepoPath, GitStatus>> {
|
fn worktree_statuses(&self) -> Option<TreeMap<RepoPath, GitFileStatus>> {
|
||||||
let state = self.state.lock();
|
let state = self.state.lock();
|
||||||
let mut map = TreeMap::default();
|
let mut map = TreeMap::default();
|
||||||
for (repo_path, status) in state.git_statuses.iter() {
|
for (repo_path, status) in state.worktree_statuses.iter() {
|
||||||
map.insert(repo_path.to_owned(), status.to_owned());
|
map.insert(repo_path.to_owned(), status.to_owned());
|
||||||
}
|
}
|
||||||
Some(map)
|
Some(map)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn file_status(&self, path: &RepoPath) -> Option<GitStatus> {
|
fn worktree_status(&self, path: &RepoPath) -> Option<GitFileStatus> {
|
||||||
let state = self.state.lock();
|
let state = self.state.lock();
|
||||||
state.git_statuses.get(path).cloned()
|
state.worktree_statuses.get(path).cloned()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -170,32 +183,11 @@ fn check_path_to_repo_path_errors(relative_file_path: &Path) -> Result<()> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Default, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
pub enum GitStatus {
|
pub enum GitFileStatus {
|
||||||
Added,
|
Added,
|
||||||
Modified,
|
Modified,
|
||||||
Conflict,
|
Conflict,
|
||||||
#[default]
|
|
||||||
Untracked,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<Status> for GitStatus {
|
|
||||||
fn from(value: Status) -> Self {
|
|
||||||
if value.contains(git2::Status::CONFLICTED) {
|
|
||||||
GitStatus::Conflict
|
|
||||||
} else if value.intersects(
|
|
||||||
git2::Status::INDEX_MODIFIED
|
|
||||||
| git2::Status::WT_MODIFIED
|
|
||||||
| git2::Status::INDEX_RENAMED
|
|
||||||
| git2::Status::WT_RENAMED,
|
|
||||||
) {
|
|
||||||
GitStatus::Modified
|
|
||||||
} else if value.intersects(git2::Status::INDEX_NEW | git2::Status::WT_NEW) {
|
|
||||||
GitStatus::Added
|
|
||||||
} else {
|
|
||||||
GitStatus::Untracked
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Ord, Hash, PartialOrd, Eq, PartialEq)]
|
#[derive(Clone, Debug, Ord, Hash, PartialOrd, Eq, PartialEq)]
|
||||||
|
|
|
@ -7,7 +7,7 @@ use client::{proto, Client};
|
||||||
use clock::ReplicaId;
|
use clock::ReplicaId;
|
||||||
use collections::{HashMap, VecDeque};
|
use collections::{HashMap, VecDeque};
|
||||||
use fs::{
|
use fs::{
|
||||||
repository::{GitRepository, GitStatus, RepoPath},
|
repository::{GitRepository, GitFileStatus, RepoPath},
|
||||||
Fs, LineEnding,
|
Fs, LineEnding,
|
||||||
};
|
};
|
||||||
use futures::{
|
use futures::{
|
||||||
|
@ -143,7 +143,7 @@ impl Snapshot {
|
||||||
pub struct RepositoryEntry {
|
pub struct RepositoryEntry {
|
||||||
pub(crate) work_directory: WorkDirectoryEntry,
|
pub(crate) work_directory: WorkDirectoryEntry,
|
||||||
pub(crate) branch: Option<Arc<str>>,
|
pub(crate) branch: Option<Arc<str>>,
|
||||||
pub(crate) statuses: TreeMap<RepoPath, GitStatus>,
|
pub(crate) worktree_statuses: TreeMap<RepoPath, GitFileStatus>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RepositoryEntry {
|
impl RepositoryEntry {
|
||||||
|
@ -165,10 +165,10 @@ impl RepositoryEntry {
|
||||||
self.work_directory.contains(snapshot, path)
|
self.work_directory.contains(snapshot, path)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn status_for(&self, snapshot: &Snapshot, path: &Path) -> Option<GitStatus> {
|
pub fn status_for(&self, snapshot: &Snapshot, path: &Path) -> Option<GitFileStatus> {
|
||||||
self.work_directory
|
self.work_directory
|
||||||
.relativize(snapshot, path)
|
.relativize(snapshot, path)
|
||||||
.and_then(|repo_path| self.statuses.get(&repo_path))
|
.and_then(|repo_path| self.worktree_statuses.get(&repo_path))
|
||||||
.cloned()
|
.cloned()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1445,7 +1445,7 @@ impl Snapshot {
|
||||||
work_directory: ProjectEntryId::from_proto(repository.work_directory_id).into(),
|
work_directory: ProjectEntryId::from_proto(repository.work_directory_id).into(),
|
||||||
branch: repository.branch.map(Into::into),
|
branch: repository.branch.map(Into::into),
|
||||||
// TODO: status
|
// TODO: status
|
||||||
statuses: Default::default(),
|
worktree_statuses: Default::default(),
|
||||||
};
|
};
|
||||||
if let Some(entry) = self.entry_for_id(repository.work_directory_id()) {
|
if let Some(entry) = self.entry_for_id(repository.work_directory_id()) {
|
||||||
self.repository_entries
|
self.repository_entries
|
||||||
|
@ -1864,7 +1864,7 @@ impl LocalSnapshot {
|
||||||
RepositoryEntry {
|
RepositoryEntry {
|
||||||
work_directory: work_dir_id.into(),
|
work_directory: work_dir_id.into(),
|
||||||
branch: repo_lock.branch_name().map(Into::into),
|
branch: repo_lock.branch_name().map(Into::into),
|
||||||
statuses: repo_lock.statuses().unwrap_or_default(),
|
worktree_statuses: repo_lock.worktree_statuses().unwrap_or_default(),
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
drop(repo_lock);
|
drop(repo_lock);
|
||||||
|
@ -2896,9 +2896,12 @@ impl BackgroundScanner {
|
||||||
.git_repositories
|
.git_repositories
|
||||||
.update(&work_dir_id, |entry| entry.scan_id = scan_id);
|
.update(&work_dir_id, |entry| entry.scan_id = scan_id);
|
||||||
|
|
||||||
|
// TODO: Status Replace linear scan with smarter sum tree traversal
|
||||||
snapshot
|
snapshot
|
||||||
.repository_entries
|
.repository_entries
|
||||||
.update(&work_dir, |entry| entry.statuses.remove(&repo_path));
|
.update(&work_dir, |entry| entry.worktree_statuses.retain(|stored_path, _| {
|
||||||
|
!stored_path.starts_with(&repo_path)
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
Some(())
|
Some(())
|
||||||
|
@ -2920,7 +2923,7 @@ impl BackgroundScanner {
|
||||||
let repo = repo.lock();
|
let repo = repo.lock();
|
||||||
repo.reload_index();
|
repo.reload_index();
|
||||||
let branch = repo.branch_name();
|
let branch = repo.branch_name();
|
||||||
let statuses = repo.statuses().unwrap_or_default();
|
let statuses = repo.worktree_statuses().unwrap_or_default();
|
||||||
|
|
||||||
snapshot.git_repositories.update(&entry_id, |entry| {
|
snapshot.git_repositories.update(&entry_id, |entry| {
|
||||||
entry.scan_id = scan_id;
|
entry.scan_id = scan_id;
|
||||||
|
@ -2929,7 +2932,7 @@ impl BackgroundScanner {
|
||||||
|
|
||||||
snapshot.repository_entries.update(&work_dir, |entry| {
|
snapshot.repository_entries.update(&work_dir, |entry| {
|
||||||
entry.branch = branch.map(Into::into);
|
entry.branch = branch.map(Into::into);
|
||||||
entry.statuses = statuses;
|
entry.worktree_statuses = statuses;
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
let repo = snapshot.repo_for(&path)?;
|
let repo = snapshot.repo_for(&path)?;
|
||||||
|
@ -2945,10 +2948,9 @@ impl BackgroundScanner {
|
||||||
}
|
}
|
||||||
|
|
||||||
let git_ptr = local_repo.repo_ptr.lock();
|
let git_ptr = local_repo.repo_ptr.lock();
|
||||||
git_ptr.file_status(&repo_path)?
|
git_ptr.worktree_status(&repo_path)?
|
||||||
};
|
};
|
||||||
|
|
||||||
if status != GitStatus::Untracked {
|
|
||||||
let work_dir = repo.work_directory(snapshot)?;
|
let work_dir = repo.work_directory(snapshot)?;
|
||||||
let work_dir_id = repo.work_directory;
|
let work_dir_id = repo.work_directory;
|
||||||
|
|
||||||
|
@ -2958,8 +2960,7 @@ impl BackgroundScanner {
|
||||||
|
|
||||||
snapshot
|
snapshot
|
||||||
.repository_entries
|
.repository_entries
|
||||||
.update(&work_dir, |entry| entry.statuses.insert(repo_path, status));
|
.update(&work_dir, |entry| entry.worktree_statuses.insert(repo_path, status));
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Some(())
|
Some(())
|
||||||
|
@ -3848,6 +3849,11 @@ mod tests {
|
||||||
"project": {
|
"project": {
|
||||||
"a.txt": "a",
|
"a.txt": "a",
|
||||||
"b.txt": "bb",
|
"b.txt": "bb",
|
||||||
|
"c": {
|
||||||
|
"d": {
|
||||||
|
"e.txt": "eee"
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
}));
|
}));
|
||||||
|
@ -3865,18 +3871,22 @@ mod tests {
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
|
cx.read(|cx| tree.read(cx).as_local().unwrap().scan_complete())
|
||||||
|
.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";
|
||||||
|
|
||||||
let work_dir = root.path().join("project");
|
let work_dir = root.path().join("project");
|
||||||
|
|
||||||
let mut repo = git_init(work_dir.as_path());
|
let mut repo = git_init(work_dir.as_path());
|
||||||
git_add(Path::new(A_TXT), &repo);
|
git_add(Path::new(A_TXT), &repo);
|
||||||
|
git_add(Path::new(E_TXT), &repo);
|
||||||
git_commit("Initial commit", &repo);
|
git_commit("Initial commit", &repo);
|
||||||
|
|
||||||
std::fs::write(work_dir.join(A_TXT), "aa").unwrap();
|
std::fs::write(work_dir.join(A_TXT), "aa").unwrap();
|
||||||
|
|
||||||
cx.read(|cx| tree.read(cx).as_local().unwrap().scan_complete())
|
|
||||||
.await;
|
|
||||||
tree.flush_fs_events(cx).await;
|
tree.flush_fs_events(cx).await;
|
||||||
|
|
||||||
// Check that the right git state is observed on startup
|
// Check that the right git state is observed on startup
|
||||||
|
@ -3886,14 +3896,14 @@ mod tests {
|
||||||
let (dir, repo) = snapshot.repository_entries.iter().next().unwrap();
|
let (dir, repo) = 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!(repo.statuses.iter().count(), 2);
|
assert_eq!(repo.worktree_statuses.iter().count(), 2);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
repo.statuses.get(&Path::new(A_TXT).into()),
|
repo.worktree_statuses.get(&Path::new(A_TXT).into()),
|
||||||
Some(&GitStatus::Modified)
|
Some(&GitFileStatus::Modified)
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
repo.statuses.get(&Path::new(B_TXT).into()),
|
repo.worktree_statuses.get(&Path::new(B_TXT).into()),
|
||||||
Some(&GitStatus::Added)
|
Some(&GitFileStatus::Added)
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -3907,14 +3917,15 @@ mod tests {
|
||||||
let snapshot = tree.snapshot();
|
let snapshot = tree.snapshot();
|
||||||
let (_, repo) = snapshot.repository_entries.iter().next().unwrap();
|
let (_, repo) = snapshot.repository_entries.iter().next().unwrap();
|
||||||
|
|
||||||
assert_eq!(repo.statuses.iter().count(), 0);
|
assert_eq!(repo.worktree_statuses.iter().count(), 0);
|
||||||
assert_eq!(repo.statuses.get(&Path::new(A_TXT).into()), None);
|
assert_eq!(repo.worktree_statuses.get(&Path::new(A_TXT).into()), None);
|
||||||
assert_eq!(repo.statuses.get(&Path::new(B_TXT).into()), None);
|
assert_eq!(repo.worktree_statuses.get(&Path::new(B_TXT).into()), None);
|
||||||
});
|
});
|
||||||
|
|
||||||
git_reset(0, &repo);
|
git_reset(0, &repo);
|
||||||
git_remove_index(Path::new(B_TXT), &repo);
|
git_remove_index(Path::new(B_TXT), &repo);
|
||||||
git_stash(&mut repo);
|
git_stash(&mut repo);
|
||||||
|
std::fs::write(work_dir.join(E_TXT), "eeee").unwrap();
|
||||||
tree.flush_fs_events(cx).await;
|
tree.flush_fs_events(cx).await;
|
||||||
|
|
||||||
// Check that more complex repo changes are tracked
|
// Check that more complex repo changes are tracked
|
||||||
|
@ -3922,17 +3933,21 @@ mod tests {
|
||||||
let snapshot = tree.snapshot();
|
let snapshot = tree.snapshot();
|
||||||
let (_, repo) = snapshot.repository_entries.iter().next().unwrap();
|
let (_, repo) = snapshot.repository_entries.iter().next().unwrap();
|
||||||
|
|
||||||
dbg!(&repo.statuses);
|
assert_eq!(repo.worktree_statuses.iter().count(), 2);
|
||||||
|
assert_eq!(repo.worktree_statuses.get(&Path::new(A_TXT).into()), None);
|
||||||
assert_eq!(repo.statuses.iter().count(), 1);
|
|
||||||
assert_eq!(repo.statuses.get(&Path::new(A_TXT).into()), None);
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
repo.statuses.get(&Path::new(B_TXT).into()),
|
repo.worktree_statuses.get(&Path::new(B_TXT).into()),
|
||||||
Some(&GitStatus::Added)
|
Some(&GitFileStatus::Added)
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
repo.worktree_statuses.get(&Path::new(E_TXT).into()),
|
||||||
|
Some(&GitFileStatus::Modified)
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
std::fs::remove_file(work_dir.join(B_TXT)).unwrap();
|
std::fs::remove_file(work_dir.join(B_TXT)).unwrap();
|
||||||
|
std::fs::remove_dir_all(work_dir.join("c")).unwrap();
|
||||||
|
|
||||||
tree.flush_fs_events(cx).await;
|
tree.flush_fs_events(cx).await;
|
||||||
|
|
||||||
// Check that non-repo behavior is tracked
|
// Check that non-repo behavior is tracked
|
||||||
|
@ -3940,9 +3955,10 @@ mod tests {
|
||||||
let snapshot = tree.snapshot();
|
let snapshot = tree.snapshot();
|
||||||
let (_, repo) = snapshot.repository_entries.iter().next().unwrap();
|
let (_, repo) = snapshot.repository_entries.iter().next().unwrap();
|
||||||
|
|
||||||
assert_eq!(repo.statuses.iter().count(), 0);
|
assert_eq!(repo.worktree_statuses.iter().count(), 0);
|
||||||
assert_eq!(repo.statuses.get(&Path::new(A_TXT).into()), None);
|
assert_eq!(repo.worktree_statuses.get(&Path::new(A_TXT).into()), None);
|
||||||
assert_eq!(repo.statuses.get(&Path::new(B_TXT).into()), None);
|
assert_eq!(repo.worktree_statuses.get(&Path::new(B_TXT).into()), None);
|
||||||
|
assert_eq!(repo.worktree_statuses.get(&Path::new(E_TXT).into()), None);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -17,7 +17,7 @@ use gpui::{
|
||||||
};
|
};
|
||||||
use menu::{Confirm, SelectNext, SelectPrev};
|
use menu::{Confirm, SelectNext, SelectPrev};
|
||||||
use project::{
|
use project::{
|
||||||
repository::GitStatus, Entry, EntryKind, Project, ProjectEntryId, ProjectPath, Worktree,
|
repository::GitFileStatus, Entry, EntryKind, Project, ProjectEntryId, ProjectPath, Worktree,
|
||||||
WorktreeId,
|
WorktreeId,
|
||||||
};
|
};
|
||||||
use settings::Settings;
|
use settings::Settings;
|
||||||
|
@ -89,7 +89,7 @@ pub struct EntryDetails {
|
||||||
is_editing: bool,
|
is_editing: bool,
|
||||||
is_processing: bool,
|
is_processing: bool,
|
||||||
is_cut: bool,
|
is_cut: bool,
|
||||||
git_status: Option<GitStatus>,
|
git_status: Option<GitFileStatus>,
|
||||||
}
|
}
|
||||||
|
|
||||||
actions!(
|
actions!(
|
||||||
|
@ -1081,19 +1081,14 @@ impl ProjectPanel {
|
||||||
|
|
||||||
// Prepare colors for git statuses
|
// Prepare colors for git statuses
|
||||||
let editor_theme = &cx.global::<Settings>().theme.editor;
|
let editor_theme = &cx.global::<Settings>().theme.editor;
|
||||||
let color_for_added = Some(editor_theme.diff.inserted);
|
|
||||||
let color_for_modified = Some(editor_theme.diff.modified);
|
|
||||||
let color_for_conflict = Some(editor_theme.diff.deleted);
|
|
||||||
let color_for_untracked = None;
|
|
||||||
let mut filename_text_style = style.text.clone();
|
let mut filename_text_style = style.text.clone();
|
||||||
filename_text_style.color = details
|
filename_text_style.color = details
|
||||||
.git_status
|
.git_status
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.and_then(|status| match status {
|
.map(|status| match status {
|
||||||
GitStatus::Added => color_for_added,
|
GitFileStatus::Added => editor_theme.diff.inserted,
|
||||||
GitStatus::Modified => color_for_modified,
|
GitFileStatus::Modified => editor_theme.diff.modified,
|
||||||
GitStatus::Conflict => color_for_conflict,
|
GitFileStatus::Conflict => editor_theme.diff.deleted,
|
||||||
GitStatus::Untracked => color_for_untracked,
|
|
||||||
})
|
})
|
||||||
.unwrap_or(style.text.color);
|
.unwrap_or(style.text.color);
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue