Compare commits
6 Commits
performanc
...
cole/git-p
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4d9de293f9 | ||
|
|
caade6af7a | ||
|
|
984eb9b5c3 | ||
|
|
00dea4ccc9 | ||
|
|
abb295ea22 | ||
|
|
33fa0724cf |
@@ -5,9 +5,9 @@ mod mac_watcher;
|
||||
pub mod fs_watcher;
|
||||
|
||||
use anyhow::{anyhow, Context as _, Result};
|
||||
#[cfg(any(test, feature = "test-support"))]
|
||||
use git::status::FileStatus;
|
||||
use git::GitHostingProviderRegistry;
|
||||
#[cfg(any(test, feature = "test-support"))]
|
||||
use git::{repository::RepoPath, status::FileStatus};
|
||||
|
||||
#[cfg(any(target_os = "linux", target_os = "freebsd"))]
|
||||
use ashpd::desktop::trash;
|
||||
@@ -892,58 +892,7 @@ impl FakeFsState {
|
||||
target: &Path,
|
||||
follow_symlink: bool,
|
||||
) -> Option<(Arc<Mutex<FakeFsEntry>>, PathBuf)> {
|
||||
let mut path = target.to_path_buf();
|
||||
let mut canonical_path = PathBuf::new();
|
||||
let mut entry_stack = Vec::new();
|
||||
'outer: loop {
|
||||
let mut path_components = path.components().peekable();
|
||||
let mut prefix = None;
|
||||
while let Some(component) = path_components.next() {
|
||||
match component {
|
||||
Component::Prefix(prefix_component) => prefix = Some(prefix_component),
|
||||
Component::RootDir => {
|
||||
entry_stack.clear();
|
||||
entry_stack.push(self.root.clone());
|
||||
canonical_path.clear();
|
||||
match prefix {
|
||||
Some(prefix_component) => {
|
||||
canonical_path = PathBuf::from(prefix_component.as_os_str());
|
||||
// Prefixes like `C:\\` are represented without their trailing slash, so we have to re-add it.
|
||||
canonical_path.push(std::path::MAIN_SEPARATOR_STR);
|
||||
}
|
||||
None => canonical_path = PathBuf::from(std::path::MAIN_SEPARATOR_STR),
|
||||
}
|
||||
}
|
||||
Component::CurDir => {}
|
||||
Component::ParentDir => {
|
||||
entry_stack.pop()?;
|
||||
canonical_path.pop();
|
||||
}
|
||||
Component::Normal(name) => {
|
||||
let current_entry = entry_stack.last().cloned()?;
|
||||
let current_entry = current_entry.lock();
|
||||
if let FakeFsEntry::Dir { entries, .. } = &*current_entry {
|
||||
let entry = entries.get(name.to_str().unwrap()).cloned()?;
|
||||
if path_components.peek().is_some() || follow_symlink {
|
||||
let entry = entry.lock();
|
||||
if let FakeFsEntry::Symlink { target, .. } = &*entry {
|
||||
let mut target = target.clone();
|
||||
target.extend(path_components);
|
||||
path = target;
|
||||
continue 'outer;
|
||||
}
|
||||
}
|
||||
entry_stack.push(entry.clone());
|
||||
canonical_path = canonical_path.join(name);
|
||||
} else {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
Some((entry_stack.pop()?, canonical_path))
|
||||
FakeFsEntry::try_read_path(&self.root, target, follow_symlink)
|
||||
}
|
||||
|
||||
fn write_path<Fn, T>(&self, path: &Path, callback: Fn) -> Result<T>
|
||||
@@ -1227,16 +1176,23 @@ impl FakeFs {
|
||||
F: FnOnce(&mut FakeGitRepositoryState),
|
||||
{
|
||||
let mut state = self.state.lock();
|
||||
let entry = state.read_path(dot_git).unwrap();
|
||||
let mut entry = entry.lock();
|
||||
let dot_git_entry = state.read_path(dot_git).unwrap();
|
||||
let mut dot_git_entry = dot_git_entry.lock();
|
||||
let parent = dot_git.parent().expect(".git has no parent path");
|
||||
let parent_entry = state.read_path(parent).unwrap();
|
||||
|
||||
if let FakeFsEntry::Dir { git_repo_state, .. } = &mut *entry {
|
||||
let repo_state = git_repo_state.get_or_insert_with(|| {
|
||||
Arc::new(Mutex::new(FakeGitRepositoryState::new(
|
||||
dot_git.to_path_buf(),
|
||||
state.git_event_tx.clone(),
|
||||
)))
|
||||
});
|
||||
if let FakeFsEntry::Dir { git_repo_state, .. } = &mut *dot_git_entry {
|
||||
let git_fs = FakeGitRepositoryFs {
|
||||
dot_git_dir: dot_git.to_owned(),
|
||||
entry: parent_entry,
|
||||
event_emitter: state.git_event_tx.clone(),
|
||||
};
|
||||
let repo_state = git_repo_state
|
||||
.get_or_insert_with(|| {
|
||||
Arc::new(Mutex::new(FakeGitRepositoryState::new(Arc::new(git_fs))))
|
||||
})
|
||||
.clone();
|
||||
drop(dot_git_entry);
|
||||
let mut repo_state = repo_state.lock();
|
||||
|
||||
f(&mut repo_state);
|
||||
@@ -1276,7 +1232,7 @@ impl FakeFs {
|
||||
state.index_contents.extend(
|
||||
head_state
|
||||
.iter()
|
||||
.map(|(path, content)| (path.to_path_buf(), content.clone())),
|
||||
.map(|(path, content)| ((*path).into(), content.clone())),
|
||||
);
|
||||
});
|
||||
}
|
||||
@@ -1284,11 +1240,9 @@ impl FakeFs {
|
||||
pub fn set_blame_for_repo(&self, dot_git: &Path, blames: Vec<(&Path, git::blame::Blame)>) {
|
||||
self.with_git_state(dot_git, true, |state| {
|
||||
state.blames.clear();
|
||||
state.blames.extend(
|
||||
blames
|
||||
.into_iter()
|
||||
.map(|(path, blame)| (path.to_path_buf(), blame)),
|
||||
);
|
||||
state
|
||||
.blames
|
||||
.extend(blames.into_iter().map(|(path, blame)| (path.into(), blame)));
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1431,6 +1385,65 @@ impl FakeFsEntry {
|
||||
Err(anyhow!("not a directory: {}", path.display()))
|
||||
}
|
||||
}
|
||||
|
||||
fn try_read_path(
|
||||
this: &Arc<Mutex<Self>>,
|
||||
target: &Path,
|
||||
follow_symlink: bool,
|
||||
) -> Option<(Arc<Mutex<FakeFsEntry>>, PathBuf)> {
|
||||
let mut path = target.to_path_buf();
|
||||
let mut canonical_path = PathBuf::new();
|
||||
let mut entry_stack = Vec::new();
|
||||
'outer: loop {
|
||||
let mut path_components = path.components().peekable();
|
||||
let mut prefix = None;
|
||||
while let Some(component) = path_components.next() {
|
||||
match component {
|
||||
Component::Prefix(prefix_component) => prefix = Some(prefix_component),
|
||||
Component::RootDir => {
|
||||
entry_stack.clear();
|
||||
entry_stack.push(this.clone());
|
||||
canonical_path.clear();
|
||||
match prefix {
|
||||
Some(prefix_component) => {
|
||||
canonical_path = PathBuf::from(prefix_component.as_os_str());
|
||||
// Prefixes like `C:\\` are represented without their trailing slash, so we have to re-add it.
|
||||
canonical_path.push(std::path::MAIN_SEPARATOR_STR);
|
||||
}
|
||||
None => canonical_path = PathBuf::from(std::path::MAIN_SEPARATOR_STR),
|
||||
}
|
||||
}
|
||||
Component::CurDir => {}
|
||||
Component::ParentDir => {
|
||||
entry_stack.pop()?;
|
||||
canonical_path.pop();
|
||||
}
|
||||
Component::Normal(name) => {
|
||||
let current_entry = entry_stack.last().cloned()?;
|
||||
let current_entry = current_entry.lock();
|
||||
if let FakeFsEntry::Dir { entries, .. } = &*current_entry {
|
||||
let entry = entries.get(name.to_str().unwrap()).cloned()?;
|
||||
if path_components.peek().is_some() || follow_symlink {
|
||||
let entry = entry.lock();
|
||||
if let FakeFsEntry::Symlink { target, .. } = &*entry {
|
||||
let mut target = target.clone();
|
||||
target.extend(path_components);
|
||||
path = target;
|
||||
continue 'outer;
|
||||
}
|
||||
}
|
||||
entry_stack.push(entry.clone());
|
||||
canonical_path = canonical_path.join(name);
|
||||
} else {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
Some((entry_stack.pop()?, canonical_path))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(test, feature = "test-support"))]
|
||||
@@ -1927,15 +1940,20 @@ impl Fs for FakeFs {
|
||||
|
||||
fn open_repo(&self, abs_dot_git: &Path) -> Option<Arc<dyn GitRepository>> {
|
||||
let state = self.state.lock();
|
||||
let entry = state.read_path(abs_dot_git).unwrap();
|
||||
let mut entry = entry.lock();
|
||||
if let FakeFsEntry::Dir { git_repo_state, .. } = &mut *entry {
|
||||
let dot_git_entry = state.read_path(abs_dot_git).unwrap();
|
||||
let mut dot_git_entry = dot_git_entry.lock();
|
||||
let parent = abs_dot_git.parent().expect(".git has no parent path");
|
||||
let parent_entry = state.read_path(parent).unwrap();
|
||||
|
||||
if let FakeFsEntry::Dir { git_repo_state, .. } = &mut *dot_git_entry {
|
||||
let git_fs = FakeGitRepositoryFs {
|
||||
dot_git_dir: abs_dot_git.to_owned(),
|
||||
entry: parent_entry,
|
||||
event_emitter: state.git_event_tx.clone(),
|
||||
};
|
||||
let state = git_repo_state
|
||||
.get_or_insert_with(|| {
|
||||
Arc::new(Mutex::new(FakeGitRepositoryState::new(
|
||||
abs_dot_git.to_path_buf(),
|
||||
state.git_event_tx.clone(),
|
||||
)))
|
||||
Arc::new(Mutex::new(FakeGitRepositoryState::new(Arc::new(git_fs))))
|
||||
})
|
||||
.clone();
|
||||
Some(git::repository::FakeGitRepository::open(state))
|
||||
@@ -1958,6 +1976,77 @@ impl Fs for FakeFs {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(test, feature = "test-support"))]
|
||||
struct FakeGitRepositoryFs {
|
||||
dot_git_dir: PathBuf,
|
||||
entry: Arc<Mutex<FakeFsEntry>>,
|
||||
event_emitter: smol::channel::Sender<PathBuf>,
|
||||
}
|
||||
|
||||
impl git::repository::FakeGitRepositoryFs for FakeGitRepositoryFs {
|
||||
fn dot_git_dir(&self) -> PathBuf {
|
||||
self.dot_git_dir.clone()
|
||||
}
|
||||
|
||||
fn read_path(&self, path: &RepoPath) -> Option<String> {
|
||||
let (entry, _) = FakeFsEntry::try_read_path(&self.entry, path, false)?;
|
||||
let content = entry.lock().file_content(path).ok()?.clone();
|
||||
let content = String::from_utf8(content).expect("Non-UTF-8 content in FakeFs entry");
|
||||
Some(content)
|
||||
}
|
||||
|
||||
fn all_paths(&self) -> Box<dyn Iterator<Item = (RepoPath, String)>> {
|
||||
let mut stack = vec![];
|
||||
let mut start = "".to_owned();
|
||||
let mut entry = self.entry.clone();
|
||||
let mut path: PathBuf = "".into();
|
||||
let mut result = Vec::new();
|
||||
'outer: loop {
|
||||
let cur = entry.clone();
|
||||
for (segment, child) in cur.lock().dir_entries(&path).unwrap().range(start..) {
|
||||
let guard = child.lock();
|
||||
match &*guard {
|
||||
FakeFsEntry::File { content, .. } => {
|
||||
let content = String::from_utf8(content.clone())
|
||||
.expect("Non-UTF-8 content in FakeFs entry");
|
||||
result.push((path.join(segment).into(), content));
|
||||
}
|
||||
FakeFsEntry::Dir {
|
||||
git_repo_state,
|
||||
entries,
|
||||
..
|
||||
} => {
|
||||
if git_repo_state.is_some()
|
||||
|| entries.keys().any(|segment| segment == ".git")
|
||||
{
|
||||
continue;
|
||||
}
|
||||
stack.push((entry.clone(), segment.clone()));
|
||||
entry = child.clone();
|
||||
start = "".to_owned();
|
||||
path = path.join(segment);
|
||||
continue 'outer;
|
||||
}
|
||||
FakeFsEntry::Symlink { .. } => {}
|
||||
}
|
||||
}
|
||||
let Some((parent, parent_start)) = stack.pop() else {
|
||||
break;
|
||||
};
|
||||
entry = parent;
|
||||
start = parent_start;
|
||||
path.pop();
|
||||
}
|
||||
Box::new(result.into_iter())
|
||||
}
|
||||
|
||||
fn repo_changed(&self) {
|
||||
self.event_emitter
|
||||
.try_send(self.dot_git_dir.clone())
|
||||
.expect("Dropped repo change notification")
|
||||
}
|
||||
}
|
||||
|
||||
fn chunks(rope: &Rope, line_ending: LineEnding) -> impl Iterator<Item = &str> {
|
||||
rope.chunks().flat_map(move |chunk| {
|
||||
let mut newline = false;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use crate::status::FileStatus;
|
||||
use crate::status::{FileStatus, StatusCode, TrackedStatus};
|
||||
use crate::GitHostingProviderRegistry;
|
||||
use crate::{blame::Blame, status::GitStatus};
|
||||
use anyhow::{anyhow, Context as _, Result};
|
||||
@@ -31,7 +31,7 @@ pub trait GitRepository: Send + Sync {
|
||||
|
||||
/// Loads a git repository entry's contents.
|
||||
/// Note that for symlink entries, this will return the contents of the symlink, not the target.
|
||||
fn load_index_text(&self, relative_file_path: &Path) -> Option<String>;
|
||||
fn load_index_text(&self, relative_file_path: &RepoPath) -> Option<String>;
|
||||
|
||||
/// Returns the URL of the remote with the given name.
|
||||
fn remote_url(&self, name: &str) -> Option<String>;
|
||||
@@ -106,7 +106,7 @@ impl GitRepository for RealGitRepository {
|
||||
repo.path().into()
|
||||
}
|
||||
|
||||
fn load_index_text(&self, relative_file_path: &Path) -> Option<String> {
|
||||
fn load_index_text(&self, relative_file_path: &RepoPath) -> Option<String> {
|
||||
fn logic(repo: &git2::Repository, relative_file_path: &Path) -> Result<Option<String>> {
|
||||
const STAGE_NORMAL: i32 = 0;
|
||||
let index = repo.index()?;
|
||||
@@ -307,17 +307,38 @@ pub struct FakeGitRepository {
|
||||
state: Arc<Mutex<FakeGitRepositoryState>>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
/// Stub for the Fs trait to break what would otherwise be a circular dependency between fs and git.
|
||||
pub trait FakeGitRepositoryFs: Send + Sync {
|
||||
fn dot_git_dir(&self) -> PathBuf;
|
||||
fn read_path(&self, path: &RepoPath) -> Option<String>;
|
||||
fn all_paths(&self) -> Box<dyn Iterator<Item = (RepoPath, String)>>;
|
||||
fn repo_changed(&self);
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct FakeGitRepositoryState {
|
||||
pub dot_git_dir: PathBuf,
|
||||
pub event_emitter: smol::channel::Sender<PathBuf>,
|
||||
pub index_contents: HashMap<PathBuf, String>,
|
||||
pub blames: HashMap<PathBuf, Blame>,
|
||||
pub fake_fs: Arc<dyn FakeGitRepositoryFs>,
|
||||
pub head_contents: HashMap<RepoPath, String>,
|
||||
pub index_contents: HashMap<RepoPath, String>,
|
||||
pub blames: HashMap<RepoPath, Blame>,
|
||||
pub statuses: HashMap<RepoPath, FileStatus>,
|
||||
pub current_branch_name: Option<String>,
|
||||
pub branches: HashSet<String>,
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for FakeGitRepositoryState {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("FakeGitRepositoryState")
|
||||
.field("head_contents", &self.head_contents)
|
||||
.field("index_contents", &self.index_contents)
|
||||
.field("blames", &self.blames)
|
||||
.field("statuses", &self.statuses)
|
||||
.field("current_branch_name", &self.current_branch_name)
|
||||
.field("branches", &self.branches)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl FakeGitRepository {
|
||||
pub fn open(state: Arc<Mutex<FakeGitRepositoryState>>) -> Arc<dyn GitRepository> {
|
||||
Arc::new(FakeGitRepository { state })
|
||||
@@ -325,10 +346,10 @@ impl FakeGitRepository {
|
||||
}
|
||||
|
||||
impl FakeGitRepositoryState {
|
||||
pub fn new(dot_git_dir: PathBuf, event_emitter: smol::channel::Sender<PathBuf>) -> Self {
|
||||
pub fn new(fake_fs: Arc<dyn FakeGitRepositoryFs>) -> Self {
|
||||
FakeGitRepositoryState {
|
||||
dot_git_dir,
|
||||
event_emitter,
|
||||
fake_fs,
|
||||
head_contents: Default::default(),
|
||||
index_contents: Default::default(),
|
||||
blames: Default::default(),
|
||||
statuses: Default::default(),
|
||||
@@ -336,12 +357,79 @@ impl FakeGitRepositoryState {
|
||||
branches: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
fn set_statuses(&mut self) {
|
||||
let mut paths = self
|
||||
.head_contents
|
||||
.iter()
|
||||
.map(|(path, contents)| (path.clone(), Some(contents.clone()), None, None))
|
||||
.chain(
|
||||
self.index_contents
|
||||
.iter()
|
||||
.map(|(path, contents)| (path.clone(), None, Some(contents.clone()), None)),
|
||||
)
|
||||
.chain(
|
||||
self.fake_fs
|
||||
.all_paths()
|
||||
.map(|(path, contents)| (path, None, None, Some(contents))),
|
||||
)
|
||||
.collect::<Vec<_>>();
|
||||
paths.sort_by(|(a, _, _, _), (b, _, _, _)| a.cmp(&b));
|
||||
paths.dedup_by(
|
||||
|(a, head_a, index_a, worktree_a), (b, head_b, index_b, worktree_b)| {
|
||||
if a != b {
|
||||
return false;
|
||||
}
|
||||
*head_b = head_b.take().or(head_a.take());
|
||||
*index_b = index_b.take().or(index_a.take());
|
||||
*worktree_b = worktree_b.take().or(worktree_a.take());
|
||||
true
|
||||
},
|
||||
);
|
||||
self.statuses.clear();
|
||||
self.statuses.extend(
|
||||
paths
|
||||
.into_iter()
|
||||
.filter_map(|(path, head, index, worktree)| {
|
||||
fn status_code(a: &Option<String>, b: &Option<String>) -> StatusCode {
|
||||
match (a, b) {
|
||||
(None, None) => StatusCode::Unmodified,
|
||||
(Some(a), Some(b)) => {
|
||||
if a == b {
|
||||
StatusCode::Modified
|
||||
} else {
|
||||
StatusCode::Unmodified
|
||||
}
|
||||
}
|
||||
(None, Some(_)) => StatusCode::Added,
|
||||
(Some(_), None) => StatusCode::Deleted,
|
||||
}
|
||||
}
|
||||
|
||||
let status = match (head, index, worktree) {
|
||||
(None, None, None) => return None,
|
||||
(Some(head), Some(index), Some(worktree))
|
||||
if head == index && index == worktree =>
|
||||
{
|
||||
return None
|
||||
}
|
||||
(None, None, Some(_)) => FileStatus::Untracked,
|
||||
(head, index, worktree) => TrackedStatus {
|
||||
index_status: status_code(&head, &index),
|
||||
worktree_status: status_code(&index, &worktree),
|
||||
}
|
||||
.into(),
|
||||
};
|
||||
Some((path, status))
|
||||
}),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl GitRepository for FakeGitRepository {
|
||||
fn reload_index(&self) {}
|
||||
|
||||
fn load_index_text(&self, path: &Path) -> Option<String> {
|
||||
fn load_index_text(&self, path: &RepoPath) -> Option<String> {
|
||||
let state = self.state.lock();
|
||||
state.index_contents.get(path).cloned()
|
||||
}
|
||||
@@ -360,8 +448,7 @@ impl GitRepository for FakeGitRepository {
|
||||
}
|
||||
|
||||
fn dot_git_dir(&self) -> PathBuf {
|
||||
let state = self.state.lock();
|
||||
state.dot_git_dir.clone()
|
||||
self.state.lock().fake_fs.dot_git_dir()
|
||||
}
|
||||
|
||||
fn status(&self, path_prefixes: &[RepoPath]) -> Result<GitStatus> {
|
||||
@@ -410,20 +497,14 @@ impl GitRepository for FakeGitRepository {
|
||||
fn change_branch(&self, name: &str) -> Result<()> {
|
||||
let mut state = self.state.lock();
|
||||
state.current_branch_name = Some(name.to_owned());
|
||||
state
|
||||
.event_emitter
|
||||
.try_send(state.dot_git_dir.clone())
|
||||
.expect("Dropped repo change event");
|
||||
state.fake_fs.repo_changed();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn create_branch(&self, name: &str) -> Result<()> {
|
||||
let mut state = self.state.lock();
|
||||
state.branches.insert(name.to_owned());
|
||||
state
|
||||
.event_emitter
|
||||
.try_send(state.dot_git_dir.clone())
|
||||
.expect("Dropped repo change event");
|
||||
state.fake_fs.repo_changed();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -436,12 +517,38 @@ impl GitRepository for FakeGitRepository {
|
||||
.cloned()
|
||||
}
|
||||
|
||||
fn stage_paths(&self, _paths: &[RepoPath]) -> Result<()> {
|
||||
unimplemented!()
|
||||
fn stage_paths(&self, paths: &[RepoPath]) -> Result<()> {
|
||||
let mut state = self.state.lock();
|
||||
for path in paths {
|
||||
match state.fake_fs.read_path(path) {
|
||||
Some(content) => {
|
||||
state.index_contents.insert(path.clone(), content);
|
||||
}
|
||||
None => {
|
||||
state.index_contents.remove(path);
|
||||
}
|
||||
}
|
||||
}
|
||||
state.fake_fs.repo_changed();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn unstage_paths(&self, _paths: &[RepoPath]) -> Result<()> {
|
||||
unimplemented!()
|
||||
fn unstage_paths(&self, paths: &[RepoPath]) -> Result<()> {
|
||||
let mut state = self.state.lock();
|
||||
for path in paths {
|
||||
let content = state.head_contents.get(path).cloned();
|
||||
match content {
|
||||
Some(content) => {
|
||||
state.index_contents.insert(path.clone(), content);
|
||||
}
|
||||
None => {
|
||||
state.index_contents.remove(path);
|
||||
}
|
||||
}
|
||||
}
|
||||
state.set_statuses();
|
||||
state.fake_fs.repo_changed();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn commit(&self, _message: &str) -> Result<()> {
|
||||
|
||||
@@ -3213,6 +3213,59 @@ async fn test_propagate_statuses_for_nested_repos(cx: &mut TestAppContext) {
|
||||
);
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_fake_git_repository_fs(cx: &mut TestAppContext) {
|
||||
init_test(cx);
|
||||
let fs = FakeFs::new(cx.background_executor.clone());
|
||||
fs.insert_tree(
|
||||
"/root",
|
||||
json!({
|
||||
"x": {
|
||||
".git": {},
|
||||
"x1.txt": "foo",
|
||||
"x2.txt": "bar",
|
||||
"y": {
|
||||
".git": {},
|
||||
"y1.txt": "baz",
|
||||
"y2.txt": "qux"
|
||||
},
|
||||
"z.txt": "sneaky..."
|
||||
},
|
||||
}),
|
||||
)
|
||||
.await;
|
||||
|
||||
fs.with_git_state("/root/x/.git".as_ref(), false, |repo_state| {
|
||||
let all_paths = repo_state.fake_fs.all_paths().collect::<Vec<_>>();
|
||||
assert_eq!(
|
||||
all_paths,
|
||||
&[
|
||||
("x1.txt".into(), "foo".to_owned()),
|
||||
("x2.txt".into(), "bar".to_owned()),
|
||||
("z.txt".into(), "sneaky...".to_owned())
|
||||
]
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
//#[gpui::test]
|
||||
//async fn test_git_status_correspondence(mut rng: StdRng, cx: &mut TestAppContext) {
|
||||
// // operations
|
||||
// // - take something that's staged and unstage it
|
||||
// // - take something that's unstaged and stage it
|
||||
// // - take something that's modified from the index and revert it
|
||||
// // - take something that's unmodified from the index and modify it
|
||||
// init_test(cx);
|
||||
// let fake = FakeFs::new(cx.background_executor.clone());
|
||||
// let initial = json!({
|
||||
// "a.txt": "a",
|
||||
// "b.txt": "b",
|
||||
// "c.txt": "c",
|
||||
// });
|
||||
// fake.insert_tree("/project", initial.clone()).await;
|
||||
// let real = temp_tree(json!({"project": initial}));
|
||||
//}
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_private_single_file_worktree(cx: &mut TestAppContext) {
|
||||
init_test(cx);
|
||||
|
||||
Reference in New Issue
Block a user