@@ -1421,6 +1421,9 @@ impl Database {
.exec(&*tx)
.await?;
+ self.update_project_worktrees(project_id, &reshared_project.worktrees, &tx)
+ .await?;
+
reshared_projects.push(ResharedProject {
id: project_id,
old_connection_id,
@@ -1970,35 +1973,7 @@ impl Database {
.await?
.ok_or_else(|| anyhow!("no such project"))?;
- if !worktrees.is_empty() {
- worktree::Entity::insert_many(worktrees.iter().map(|worktree| {
- worktree::ActiveModel {
- id: ActiveValue::set(worktree.id as i64),
- project_id: ActiveValue::set(project.id),
- abs_path: ActiveValue::set(worktree.abs_path.clone()),
- root_name: ActiveValue::set(worktree.root_name.clone()),
- visible: ActiveValue::set(worktree.visible),
- scan_id: ActiveValue::set(0),
- is_complete: ActiveValue::set(false),
- }
- }))
- .on_conflict(
- OnConflict::columns([worktree::Column::ProjectId, worktree::Column::Id])
- .update_column(worktree::Column::RootName)
- .to_owned(),
- )
- .exec(&*tx)
- .await?;
- }
-
- worktree::Entity::delete_many()
- .filter(
- worktree::Column::ProjectId.eq(project.id).and(
- worktree::Column::Id
- .is_not_in(worktrees.iter().map(|worktree| worktree.id as i64)),
- ),
- )
- .exec(&*tx)
+ self.update_project_worktrees(project.id, worktrees, &tx)
.await?;
let guest_connection_ids = self.project_guest_connection_ids(project.id, &tx).await?;
@@ -2008,6 +1983,41 @@ impl Database {
.await
}
+ async fn update_project_worktrees(
+ &self,
+ project_id: ProjectId,
+ worktrees: &[proto::WorktreeMetadata],
+ tx: &DatabaseTransaction,
+ ) -> Result<()> {
+ if !worktrees.is_empty() {
+ worktree::Entity::insert_many(worktrees.iter().map(|worktree| worktree::ActiveModel {
+ id: ActiveValue::set(worktree.id as i64),
+ project_id: ActiveValue::set(project_id),
+ abs_path: ActiveValue::set(worktree.abs_path.clone()),
+ root_name: ActiveValue::set(worktree.root_name.clone()),
+ visible: ActiveValue::set(worktree.visible),
+ scan_id: ActiveValue::set(0),
+ is_complete: ActiveValue::set(false),
+ }))
+ .on_conflict(
+ OnConflict::columns([worktree::Column::ProjectId, worktree::Column::Id])
+ .update_column(worktree::Column::RootName)
+ .to_owned(),
+ )
+ .exec(&*tx)
+ .await?;
+ }
+
+ worktree::Entity::delete_many()
+ .filter(worktree::Column::ProjectId.eq(project_id).and(
+ worktree::Column::Id.is_not_in(worktrees.iter().map(|worktree| worktree.id as i64)),
+ ))
+ .exec(&*tx)
+ .await?;
+
+ Ok(())
+ }
+
pub async fn update_worktree(
&self,
update: &proto::UpdateWorktree,
@@ -1336,7 +1336,9 @@ async fn test_host_reconnect(
}
},
"dir2": {
- "x.txt": "x-contents",
+ "x": "x-contents",
+ "y": "y-contents",
+ "z": "z-contents",
},
}),
)
@@ -1344,7 +1346,8 @@ async fn test_host_reconnect(
let active_call_a = cx_a.read(ActiveCall::global);
let (project_a, _) = client_a.build_local_project("/root/dir1", cx_a).await;
- let worktree_a = project_a.read_with(cx_a, |project, cx| project.worktrees(cx).next().unwrap());
+ let worktree_a1 =
+ project_a.read_with(cx_a, |project, cx| project.worktrees(cx).next().unwrap());
let project_id = active_call_a
.update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
.await
@@ -1353,7 +1356,7 @@ async fn test_host_reconnect(
let project_b = client_b.build_remote_project(project_id, cx_b).await;
deterministic.run_until_parked();
- let worktree_id = worktree_a.read_with(cx_a, |worktree, _| {
+ let worktree1_id = worktree_a1.read_with(cx_a, |worktree, _| {
assert!(worktree.as_local().unwrap().is_shared());
worktree.id()
});
@@ -1370,11 +1373,11 @@ async fn test_host_reconnect(
assert!(!project.is_read_only());
assert_eq!(project.collaborators().len(), 1);
});
- worktree_a.read_with(cx_a, |tree, _| {
+ worktree_a1.read_with(cx_a, |tree, _| {
assert!(tree.as_local().unwrap().is_shared())
});
- // While disconnected, add and remove files from the client A's project.
+ // While disconnected, add/remove files and worktrees from client A's project.
client_a
.fs
.insert_tree(
@@ -1398,6 +1401,20 @@ async fn test_host_reconnect(
)
.await
.unwrap();
+ let (worktree_a2, _) = project_a
+ .update(cx_a, |p, cx| {
+ p.find_or_create_local_worktree("/root/dir2", true, cx)
+ })
+ .await
+ .unwrap();
+ worktree_a2
+ .read_with(cx_a, |tree, _| tree.as_local().unwrap().scan_complete())
+ .await;
+ let worktree2_id = worktree_a2.read_with(cx_a, |tree, _| {
+ assert!(tree.as_local().unwrap().is_shared());
+ tree.id()
+ });
+ deterministic.run_until_parked();
// Client A reconnects. Their project is re-shared, and client B re-joins it.
server.allow_connections();
@@ -1409,7 +1426,7 @@ async fn test_host_reconnect(
project_a.read_with(cx_a, |project, cx| {
assert!(project.is_shared());
assert_eq!(
- worktree_a
+ worktree_a1
.read(cx)
.snapshot()
.paths()
@@ -1429,12 +1446,22 @@ async fn test_host_reconnect(
"subdir2/i.txt"
]
);
+ assert_eq!(
+ worktree_a2
+ .read(cx)
+ .snapshot()
+ .paths()
+ .map(|p| p.to_str().unwrap())
+ .collect::<Vec<_>>(),
+ vec!["x", "y", "z"]
+ );
});
project_b.read_with(cx_b, |project, cx| {
assert!(!project.is_read_only());
- let worktree_b = project.worktree_for_id(worktree_id, cx).unwrap();
assert_eq!(
- worktree_b
+ project
+ .worktree_for_id(worktree1_id, cx)
+ .unwrap()
.read(cx)
.snapshot()
.paths()
@@ -1454,6 +1481,17 @@ async fn test_host_reconnect(
"subdir2/i.txt"
]
);
+ assert_eq!(
+ project
+ .worktree_for_id(worktree2_id, cx)
+ .unwrap()
+ .read(cx)
+ .snapshot()
+ .paths()
+ .map(|p| p.to_str().unwrap())
+ .collect::<Vec<_>>(),
+ vec!["x", "y", "z"]
+ );
});
}
@@ -1028,28 +1028,16 @@ impl Project {
cx.emit(Event::RemoteIdChanged(Some(project_id)));
cx.notify();
- let mut status = self.client.status();
let (metadata_changed_tx, mut metadata_changed_rx) = mpsc::unbounded();
self.client_state = Some(ProjectClientState::Local {
remote_id: project_id,
metadata_changed: metadata_changed_tx,
_maintain_metadata: cx.spawn_weak(move |this, cx| async move {
let mut txs = Vec::new();
- loop {
- select_biased! {
- tx = metadata_changed_rx.next().fuse() => {
- let Some(tx) = tx else { break };
- txs.push(tx);
- while let Ok(Some(next_tx)) = metadata_changed_rx.try_next() {
- txs.push(next_tx);
- }
- }
- status = status.next().fuse() => {
- let Some(status) = status else { break };
- if !status.is_connected() {
- continue
- }
- }
+ while let Some(tx) = metadata_changed_rx.next().await {
+ txs.push(tx);
+ while let Ok(Some(next_tx)) = metadata_changed_rx.try_next() {
+ txs.push(next_tx);
}
let Some(this) = this.upgrade(&cx) else { break };
@@ -4284,12 +4272,13 @@ impl Project {
if let Some(project_id) =
project.read_with(&cx, |project, _| project.remote_id())
{
- worktree
- .update(&mut cx, |worktree, cx| {
- worktree.as_local_mut().unwrap().share(project_id, cx)
- })
- .await
- .log_err();
+ worktree.update(&mut cx, |worktree, cx| {
+ worktree
+ .as_local_mut()
+ .unwrap()
+ .share(project_id, cx)
+ .detach_and_log_err(cx);
+ });
}
Ok(worktree)