mirror of
https://github.com/zed-industries/zed.git
synced 2026-06-01 03:14:56 +07:00
Maintain root repo common dir path as a field on Worktree (#53023)
This enables us to always different git worktrees of the same repo together. Depends on https://github.com/zed-industries/cloud/pull/2220 Release Notes: - N/A --------- Co-authored-by: Eric Holk <eric@zed.dev>
This commit is contained in:
parent
134dec8f95
commit
20f7308677
17 changed files with 297 additions and 4 deletions
|
|
@ -65,6 +65,7 @@ CREATE TABLE "worktrees" (
|
|||
"scan_id" INTEGER NOT NULL,
|
||||
"is_complete" BOOL NOT NULL DEFAULT FALSE,
|
||||
"completed_scan_id" INTEGER NOT NULL,
|
||||
"root_repo_common_dir" VARCHAR,
|
||||
PRIMARY KEY (project_id, id)
|
||||
);
|
||||
|
||||
|
|
|
|||
|
|
@ -484,7 +484,8 @@ CREATE TABLE public.worktrees (
|
|||
visible boolean NOT NULL,
|
||||
scan_id bigint NOT NULL,
|
||||
is_complete boolean DEFAULT false NOT NULL,
|
||||
completed_scan_id bigint
|
||||
completed_scan_id bigint,
|
||||
root_repo_common_dir character varying
|
||||
);
|
||||
|
||||
ALTER TABLE ONLY public.breakpoints ALTER COLUMN id SET DEFAULT nextval('public.breakpoints_id_seq'::regclass);
|
||||
|
|
|
|||
|
|
@ -559,6 +559,7 @@ pub struct RejoinedWorktree {
|
|||
pub settings_files: Vec<WorktreeSettingsFile>,
|
||||
pub scan_id: u64,
|
||||
pub completed_scan_id: u64,
|
||||
pub root_repo_common_dir: Option<String>,
|
||||
}
|
||||
|
||||
pub struct LeftRoom {
|
||||
|
|
@ -638,6 +639,7 @@ pub struct Worktree {
|
|||
pub settings_files: Vec<WorktreeSettingsFile>,
|
||||
pub scan_id: u64,
|
||||
pub completed_scan_id: u64,
|
||||
pub root_repo_common_dir: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
|
|
|
|||
|
|
@ -87,6 +87,7 @@ impl Database {
|
|||
visible: ActiveValue::set(worktree.visible),
|
||||
scan_id: ActiveValue::set(0),
|
||||
completed_scan_id: ActiveValue::set(0),
|
||||
root_repo_common_dir: ActiveValue::set(None),
|
||||
}
|
||||
}))
|
||||
.exec(&*tx)
|
||||
|
|
@ -203,6 +204,7 @@ impl Database {
|
|||
visible: ActiveValue::set(worktree.visible),
|
||||
scan_id: ActiveValue::set(0),
|
||||
completed_scan_id: ActiveValue::set(0),
|
||||
root_repo_common_dir: ActiveValue::set(None),
|
||||
}))
|
||||
.on_conflict(
|
||||
OnConflict::columns([worktree::Column::ProjectId, worktree::Column::Id])
|
||||
|
|
@ -266,6 +268,7 @@ impl Database {
|
|||
ActiveValue::default()
|
||||
},
|
||||
abs_path: ActiveValue::set(update.abs_path.clone()),
|
||||
root_repo_common_dir: ActiveValue::set(update.root_repo_common_dir.clone()),
|
||||
..Default::default()
|
||||
})
|
||||
.exec(&*tx)
|
||||
|
|
@ -761,6 +764,7 @@ impl Database {
|
|||
settings_files: Default::default(),
|
||||
scan_id: db_worktree.scan_id as u64,
|
||||
completed_scan_id: db_worktree.completed_scan_id as u64,
|
||||
root_repo_common_dir: db_worktree.root_repo_common_dir,
|
||||
legacy_repository_entries: Default::default(),
|
||||
},
|
||||
)
|
||||
|
|
|
|||
|
|
@ -629,6 +629,7 @@ impl Database {
|
|||
settings_files: Default::default(),
|
||||
scan_id: db_worktree.scan_id as u64,
|
||||
completed_scan_id: db_worktree.completed_scan_id as u64,
|
||||
root_repo_common_dir: db_worktree.root_repo_common_dir,
|
||||
};
|
||||
|
||||
let rejoined_worktree = rejoined_project
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ pub struct Model {
|
|||
pub scan_id: i64,
|
||||
/// The last scan that fully completed.
|
||||
pub completed_scan_id: i64,
|
||||
pub root_repo_common_dir: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
|
||||
|
|
|
|||
|
|
@ -1485,6 +1485,7 @@ fn notify_rejoined_projects(
|
|||
worktree_id: worktree.id,
|
||||
abs_path: worktree.abs_path.clone(),
|
||||
root_name: worktree.root_name,
|
||||
root_repo_common_dir: worktree.root_repo_common_dir,
|
||||
updated_entries: worktree.updated_entries,
|
||||
removed_entries: worktree.removed_entries,
|
||||
scan_id: worktree.scan_id,
|
||||
|
|
@ -1943,6 +1944,7 @@ async fn join_project(
|
|||
worktree_id,
|
||||
abs_path: worktree.abs_path.clone(),
|
||||
root_name: worktree.root_name,
|
||||
root_repo_common_dir: worktree.root_repo_common_dir,
|
||||
updated_entries: worktree.entries,
|
||||
removed_entries: Default::default(),
|
||||
scan_id: worktree.scan_id,
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
use std::path::{Path, PathBuf};
|
||||
use std::path::{self, Path, PathBuf};
|
||||
|
||||
use call::ActiveCall;
|
||||
use client::RECEIVE_TIMEOUT;
|
||||
|
|
@ -17,6 +17,61 @@ use workspace::{MultiWorkspace, Workspace};
|
|||
|
||||
use crate::TestServer;
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_root_repo_common_dir_sync(
|
||||
executor: BackgroundExecutor,
|
||||
cx_a: &mut TestAppContext,
|
||||
cx_b: &mut TestAppContext,
|
||||
) {
|
||||
let mut server = TestServer::start(executor.clone()).await;
|
||||
let client_a = server.create_client(cx_a, "user_a").await;
|
||||
let client_b = server.create_client(cx_b, "user_b").await;
|
||||
server
|
||||
.create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
|
||||
.await;
|
||||
let active_call_a = cx_a.read(ActiveCall::global);
|
||||
|
||||
// Set up a project whose root IS a git repository.
|
||||
client_a
|
||||
.fs()
|
||||
.insert_tree(
|
||||
path!("/project"),
|
||||
json!({ ".git": {}, "file.txt": "content" }),
|
||||
)
|
||||
.await;
|
||||
|
||||
let (project_a, _) = client_a.build_local_project(path!("/project"), cx_a).await;
|
||||
executor.run_until_parked();
|
||||
|
||||
// Host should see root_repo_common_dir pointing to .git at the root.
|
||||
let host_common_dir = project_a.read_with(cx_a, |project, cx| {
|
||||
let worktree = project.worktrees(cx).next().unwrap();
|
||||
worktree.read(cx).snapshot().root_repo_common_dir().cloned()
|
||||
});
|
||||
assert_eq!(
|
||||
host_common_dir.as_deref(),
|
||||
Some(path::Path::new(path!("/project/.git"))),
|
||||
);
|
||||
|
||||
// Share the project and have client B join.
|
||||
let project_id = active_call_a
|
||||
.update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
|
||||
.await
|
||||
.unwrap();
|
||||
let project_b = client_b.join_remote_project(project_id, cx_b).await;
|
||||
executor.run_until_parked();
|
||||
|
||||
// Guest should see the same root_repo_common_dir as the host.
|
||||
let guest_common_dir = project_b.read_with(cx_b, |project, cx| {
|
||||
let worktree = project.worktrees(cx).next().unwrap();
|
||||
worktree.read(cx).snapshot().root_repo_common_dir().cloned()
|
||||
});
|
||||
assert_eq!(
|
||||
guest_common_dir, host_common_dir,
|
||||
"guest should see the same root_repo_common_dir as host",
|
||||
);
|
||||
}
|
||||
|
||||
fn collect_diff_stats<C: gpui::AppContext>(
|
||||
panel: &gpui::Entity<GitPanel>,
|
||||
cx: &C,
|
||||
|
|
|
|||
|
|
@ -319,6 +319,7 @@ impl LicenseDetectionWatcher {
|
|||
}
|
||||
worktree::Event::DeletedEntry(_)
|
||||
| worktree::Event::UpdatedGitRepositories(_)
|
||||
| worktree::Event::UpdatedRootRepoCommonDir
|
||||
| worktree::Event::Deleted => {}
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -4414,7 +4414,8 @@ impl LspStore {
|
|||
}
|
||||
worktree::Event::UpdatedGitRepositories(_)
|
||||
| worktree::Event::DeletedEntry(_)
|
||||
| worktree::Event::Deleted => {}
|
||||
| worktree::Event::Deleted
|
||||
| worktree::Event::UpdatedRootRepoCommonDir => {}
|
||||
})
|
||||
.detach()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -59,7 +59,7 @@ impl WorktreeRoots {
|
|||
let path = TriePath::from(entry.path.as_ref());
|
||||
this.roots.remove(&path);
|
||||
}
|
||||
WorktreeEvent::Deleted => {}
|
||||
WorktreeEvent::Deleted | WorktreeEvent::UpdatedRootRepoCommonDir => {}
|
||||
}
|
||||
}),
|
||||
})
|
||||
|
|
|
|||
|
|
@ -812,6 +812,7 @@ impl WorktreeStore {
|
|||
// The worktree root itself has been deleted (for single-file worktrees)
|
||||
// The worktree will be removed via the observe_release callback
|
||||
}
|
||||
worktree::Event::UpdatedRootRepoCommonDir => {}
|
||||
}
|
||||
})
|
||||
.detach();
|
||||
|
|
|
|||
|
|
@ -225,6 +225,7 @@ message UpdateWorktree {
|
|||
uint64 scan_id = 8;
|
||||
bool is_last_update = 9;
|
||||
string abs_path = 10;
|
||||
optional string root_repo_common_dir = 11;
|
||||
}
|
||||
|
||||
// deprecated
|
||||
|
|
|
|||
|
|
@ -881,6 +881,7 @@ pub fn split_worktree_update(mut message: UpdateWorktree) -> impl Iterator<Item
|
|||
worktree_id: message.worktree_id,
|
||||
root_name: message.root_name.clone(),
|
||||
abs_path: message.abs_path.clone(),
|
||||
root_repo_common_dir: message.root_repo_common_dir.clone(),
|
||||
updated_entries,
|
||||
removed_entries,
|
||||
scan_id: message.scan_id,
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ use languages::rust_lang;
|
|||
|
||||
use extension::ExtensionHostProxy;
|
||||
use fs::{FakeFs, Fs};
|
||||
use git::repository::Worktree as GitWorktree;
|
||||
use gpui::{AppContext as _, Entity, SharedString, TestAppContext};
|
||||
use http_client::{BlockedHttpClient, FakeHttpClient};
|
||||
use language::{
|
||||
|
|
@ -1539,6 +1540,87 @@ async fn test_copy_file_into_remote_project(
|
|||
);
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_remote_root_repo_common_dir(cx: &mut TestAppContext, server_cx: &mut TestAppContext) {
|
||||
let fs = FakeFs::new(server_cx.executor());
|
||||
fs.insert_tree(
|
||||
"/code",
|
||||
json!({
|
||||
"main_repo": {
|
||||
".git": {},
|
||||
"file.txt": "content",
|
||||
},
|
||||
"no_git": {
|
||||
"file.txt": "content",
|
||||
},
|
||||
}),
|
||||
)
|
||||
.await;
|
||||
|
||||
// Create a linked worktree that points back to main_repo's .git.
|
||||
fs.add_linked_worktree_for_repo(
|
||||
Path::new("/code/main_repo/.git"),
|
||||
false,
|
||||
GitWorktree {
|
||||
path: PathBuf::from("/code/linked_worktree"),
|
||||
ref_name: Some("refs/heads/feature-branch".into()),
|
||||
sha: "abc123".into(),
|
||||
is_main: false,
|
||||
},
|
||||
)
|
||||
.await;
|
||||
|
||||
let (project, _headless) = init_test(&fs, cx, server_cx).await;
|
||||
|
||||
// Main repo: root_repo_common_dir should be the .git directory itself.
|
||||
let (worktree_main, _) = project
|
||||
.update(cx, |project, cx| {
|
||||
project.find_or_create_worktree("/code/main_repo", true, cx)
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
cx.executor().run_until_parked();
|
||||
|
||||
let common_dir = worktree_main.read_with(cx, |worktree, _| {
|
||||
worktree.snapshot().root_repo_common_dir().cloned()
|
||||
});
|
||||
assert_eq!(
|
||||
common_dir.as_deref(),
|
||||
Some(Path::new("/code/main_repo/.git")),
|
||||
);
|
||||
|
||||
// Linked worktree: root_repo_common_dir should point to the main repo's .git.
|
||||
let (worktree_linked, _) = project
|
||||
.update(cx, |project, cx| {
|
||||
project.find_or_create_worktree("/code/linked_worktree", true, cx)
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
cx.executor().run_until_parked();
|
||||
|
||||
let common_dir = worktree_linked.read_with(cx, |worktree, _| {
|
||||
worktree.snapshot().root_repo_common_dir().cloned()
|
||||
});
|
||||
assert_eq!(
|
||||
common_dir.as_deref(),
|
||||
Some(Path::new("/code/main_repo/.git")),
|
||||
);
|
||||
|
||||
// No git repo: root_repo_common_dir should be None.
|
||||
let (worktree_no_git, _) = project
|
||||
.update(cx, |project, cx| {
|
||||
project.find_or_create_worktree("/code/no_git", true, cx)
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
cx.executor().run_until_parked();
|
||||
|
||||
let common_dir = worktree_no_git.read_with(cx, |worktree, _| {
|
||||
worktree.snapshot().root_repo_common_dir().cloned()
|
||||
});
|
||||
assert_eq!(common_dir, None);
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_remote_git_diffs(cx: &mut TestAppContext, server_cx: &mut TestAppContext) {
|
||||
let text_2 = "
|
||||
|
|
|
|||
|
|
@ -176,6 +176,7 @@ pub struct Snapshot {
|
|||
root_char_bag: CharBag,
|
||||
entries_by_path: SumTree<Entry>,
|
||||
entries_by_id: SumTree<PathEntry>,
|
||||
root_repo_common_dir: Option<Arc<SanitizedPath>>,
|
||||
always_included_entries: Vec<Arc<RelPath>>,
|
||||
|
||||
/// A number that increases every time the worktree begins scanning
|
||||
|
|
@ -368,6 +369,7 @@ struct UpdateObservationState {
|
|||
pub enum Event {
|
||||
UpdatedEntries(UpdatedEntriesSet),
|
||||
UpdatedGitRepositories(UpdatedGitRepositoriesSet),
|
||||
UpdatedRootRepoCommonDir,
|
||||
DeletedEntry(ProjectEntryId),
|
||||
/// The worktree root itself has been deleted (for single-file worktrees)
|
||||
Deleted,
|
||||
|
|
@ -407,6 +409,10 @@ impl Worktree {
|
|||
None
|
||||
};
|
||||
|
||||
let root_repo_common_dir = discover_root_repo_common_dir(&abs_path, fs.as_ref())
|
||||
.await
|
||||
.map(SanitizedPath::from_arc);
|
||||
|
||||
Ok(cx.new(move |cx: &mut Context<Worktree>| {
|
||||
let mut snapshot = LocalSnapshot {
|
||||
ignores_by_parent_abs_path: Default::default(),
|
||||
|
|
@ -426,6 +432,7 @@ impl Worktree {
|
|||
),
|
||||
root_file_handle,
|
||||
};
|
||||
snapshot.root_repo_common_dir = root_repo_common_dir;
|
||||
|
||||
let worktree_id = snapshot.id();
|
||||
let settings_location = Some(SettingsLocation {
|
||||
|
|
@ -564,6 +571,7 @@ impl Worktree {
|
|||
this.update(cx, |this, cx| {
|
||||
let mut entries_changed = false;
|
||||
let this = this.as_remote_mut().unwrap();
|
||||
let old_root_repo_common_dir = this.snapshot.root_repo_common_dir.clone();
|
||||
{
|
||||
let mut lock = this.background_snapshot.lock();
|
||||
this.snapshot = lock.0.clone();
|
||||
|
|
@ -579,6 +587,9 @@ impl Worktree {
|
|||
if entries_changed {
|
||||
cx.emit(Event::UpdatedEntries(Arc::default()));
|
||||
}
|
||||
if this.snapshot.root_repo_common_dir != old_root_repo_common_dir {
|
||||
cx.emit(Event::UpdatedRootRepoCommonDir);
|
||||
}
|
||||
cx.notify();
|
||||
while let Some((scan_id, _)) = this.snapshot_subscriptions.front() {
|
||||
if this.observed_snapshot(*scan_id) {
|
||||
|
|
@ -1183,6 +1194,13 @@ impl LocalWorktree {
|
|||
cx: &mut Context<Worktree>,
|
||||
) {
|
||||
let repo_changes = self.changed_repos(&self.snapshot, &mut new_snapshot);
|
||||
|
||||
new_snapshot.root_repo_common_dir = new_snapshot
|
||||
.local_repo_for_work_directory_path(RelPath::empty())
|
||||
.map(|repo| SanitizedPath::from_arc(repo.common_dir_abs_path.clone()));
|
||||
|
||||
let root_repo_common_dir_changed =
|
||||
self.snapshot.root_repo_common_dir != new_snapshot.root_repo_common_dir;
|
||||
self.snapshot = new_snapshot;
|
||||
|
||||
if let Some(share) = self.update_observer.as_mut() {
|
||||
|
|
@ -1198,6 +1216,9 @@ impl LocalWorktree {
|
|||
if !repo_changes.is_empty() {
|
||||
cx.emit(Event::UpdatedGitRepositories(repo_changes));
|
||||
}
|
||||
if root_repo_common_dir_changed {
|
||||
cx.emit(Event::UpdatedRootRepoCommonDir);
|
||||
}
|
||||
|
||||
while let Some((scan_id, _)) = self.snapshot_subscriptions.front() {
|
||||
if self.snapshot.completed_scan_id >= *scan_id {
|
||||
|
|
@ -2216,6 +2237,7 @@ impl Snapshot {
|
|||
always_included_entries: Default::default(),
|
||||
entries_by_path: Default::default(),
|
||||
entries_by_id: Default::default(),
|
||||
root_repo_common_dir: None,
|
||||
scan_id: 1,
|
||||
completed_scan_id: 0,
|
||||
}
|
||||
|
|
@ -2241,6 +2263,12 @@ impl Snapshot {
|
|||
SanitizedPath::cast_arc_ref(&self.abs_path)
|
||||
}
|
||||
|
||||
pub fn root_repo_common_dir(&self) -> Option<&Arc<Path>> {
|
||||
self.root_repo_common_dir
|
||||
.as_ref()
|
||||
.map(SanitizedPath::cast_arc_ref)
|
||||
}
|
||||
|
||||
fn build_initial_update(&self, project_id: u64, worktree_id: u64) -> proto::UpdateWorktree {
|
||||
let mut updated_entries = self
|
||||
.entries_by_path
|
||||
|
|
@ -2254,6 +2282,9 @@ impl Snapshot {
|
|||
worktree_id,
|
||||
abs_path: self.abs_path().to_string_lossy().into_owned(),
|
||||
root_name: self.root_name().to_proto(),
|
||||
root_repo_common_dir: self
|
||||
.root_repo_common_dir()
|
||||
.map(|p| p.to_string_lossy().into_owned()),
|
||||
updated_entries,
|
||||
removed_entries: Vec::new(),
|
||||
scan_id: self.scan_id as u64,
|
||||
|
|
@ -2399,6 +2430,10 @@ impl Snapshot {
|
|||
self.entries_by_path.edit(entries_by_path_edits, ());
|
||||
self.entries_by_id.edit(entries_by_id_edits, ());
|
||||
|
||||
self.root_repo_common_dir = update
|
||||
.root_repo_common_dir
|
||||
.map(|p| SanitizedPath::new_arc(Path::new(&p)));
|
||||
|
||||
self.scan_id = update.scan_id as usize;
|
||||
if update.is_last_update {
|
||||
self.completed_scan_id = update.scan_id as usize;
|
||||
|
|
@ -2627,6 +2662,9 @@ impl LocalSnapshot {
|
|||
worktree_id,
|
||||
abs_path: self.abs_path().to_string_lossy().into_owned(),
|
||||
root_name: self.root_name().to_proto(),
|
||||
root_repo_common_dir: self
|
||||
.root_repo_common_dir()
|
||||
.map(|p| p.to_string_lossy().into_owned()),
|
||||
updated_entries,
|
||||
removed_entries,
|
||||
scan_id: self.scan_id as u64,
|
||||
|
|
@ -6071,6 +6109,16 @@ fn parse_gitfile(content: &str) -> anyhow::Result<&Path> {
|
|||
Ok(Path::new(path.trim()))
|
||||
}
|
||||
|
||||
async fn discover_root_repo_common_dir(root_abs_path: &Path, fs: &dyn Fs) -> Option<Arc<Path>> {
|
||||
let root_dot_git = root_abs_path.join(DOT_GIT);
|
||||
if !fs.metadata(&root_dot_git).await.is_ok_and(|m| m.is_some()) {
|
||||
return None;
|
||||
}
|
||||
let dot_git_path: Arc<Path> = root_dot_git.into();
|
||||
let (_, common_dir) = discover_git_paths(&dot_git_path, fs).await;
|
||||
Some(common_dir)
|
||||
}
|
||||
|
||||
async fn discover_git_paths(dot_git_abs_path: &Arc<Path>, fs: &dyn Fs) -> (Arc<Path>, Arc<Path>) {
|
||||
let mut repository_dir_abs_path = dot_git_abs_path.clone();
|
||||
let mut common_dir_abs_path = dot_git_abs_path.clone();
|
||||
|
|
|
|||
|
|
@ -2736,6 +2736,97 @@ fn check_worktree_entries(
|
|||
}
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_root_repo_common_dir(executor: BackgroundExecutor, cx: &mut TestAppContext) {
|
||||
init_test(cx);
|
||||
|
||||
use git::repository::Worktree as GitWorktree;
|
||||
|
||||
let fs = FakeFs::new(executor);
|
||||
|
||||
// Set up a main repo and a linked worktree pointing back to it.
|
||||
fs.insert_tree(
|
||||
path!("/main_repo"),
|
||||
json!({
|
||||
".git": {},
|
||||
"file.txt": "content",
|
||||
}),
|
||||
)
|
||||
.await;
|
||||
fs.add_linked_worktree_for_repo(
|
||||
Path::new(path!("/main_repo/.git")),
|
||||
false,
|
||||
GitWorktree {
|
||||
path: PathBuf::from(path!("/linked_worktree")),
|
||||
ref_name: Some("refs/heads/feature".into()),
|
||||
sha: "abc123".into(),
|
||||
is_main: false,
|
||||
},
|
||||
)
|
||||
.await;
|
||||
fs.write(
|
||||
path!("/linked_worktree/file.txt").as_ref(),
|
||||
"content".as_bytes(),
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let tree = Worktree::local(
|
||||
path!("/linked_worktree").as_ref(),
|
||||
true,
|
||||
fs.clone(),
|
||||
Arc::default(),
|
||||
true,
|
||||
WorktreeId::from_proto(0),
|
||||
&mut cx.to_async(),
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
tree.update(cx, |tree, _| tree.as_local().unwrap().scan_complete())
|
||||
.await;
|
||||
cx.run_until_parked();
|
||||
|
||||
// For a linked worktree, root_repo_common_dir should point to the
|
||||
// main repo's .git, not the worktree-specific git directory.
|
||||
tree.read_with(cx, |tree, _| {
|
||||
assert_eq!(
|
||||
tree.snapshot().root_repo_common_dir().map(|p| p.as_ref()),
|
||||
Some(Path::new(path!("/main_repo/.git"))),
|
||||
);
|
||||
});
|
||||
|
||||
let event_count: Rc<Cell<usize>> = Rc::new(Cell::new(0));
|
||||
tree.update(cx, {
|
||||
let event_count = event_count.clone();
|
||||
|_, cx| {
|
||||
cx.subscribe(&cx.entity(), move |_, _, event, _| {
|
||||
if matches!(event, Event::UpdatedRootRepoCommonDir) {
|
||||
event_count.set(event_count.get() + 1);
|
||||
}
|
||||
})
|
||||
.detach();
|
||||
}
|
||||
});
|
||||
|
||||
// Remove .git — root_repo_common_dir should become None.
|
||||
fs.remove_file(
|
||||
&PathBuf::from(path!("/linked_worktree/.git")),
|
||||
Default::default(),
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
tree.flush_fs_events(cx).await;
|
||||
|
||||
tree.read_with(cx, |tree, _| {
|
||||
assert_eq!(tree.snapshot().root_repo_common_dir(), None);
|
||||
});
|
||||
assert_eq!(
|
||||
event_count.get(),
|
||||
1,
|
||||
"should have emitted UpdatedRootRepoCommonDir on removal"
|
||||
);
|
||||
}
|
||||
|
||||
fn init_test(cx: &mut gpui::TestAppContext) {
|
||||
zlog::init_test();
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue