collab: Fix project sharing between Windows and Unix (#23680)

Closes #14258

Windows user(host) sharing a project to a guest(using macOS), and host
follows guest:


https://github.com/user-attachments/assets/ba306b6b-23f7-48b1-8ba8-fdc5992d8f00

macOS user(host) sharing a project to a guest(using Windows), and host
follows guest:



https://github.com/user-attachments/assets/c5ee5e78-870d-49e5-907d-8565977a01ae

macOS user edits files in a windows project through collab:



https://github.com/user-attachments/assets/581057cf-e7df-4e56-a0ce-ced74339906a





Release Notes:

- N/A
This commit is contained in:
张小白 2025-02-11 08:12:01 +08:00 committed by GitHub
parent 929c5e76b4
commit c1f162abc6
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
14 changed files with 226 additions and 117 deletions

View file

@ -39,7 +39,7 @@ use postage::{
watch,
};
use rpc::{
proto::{self, split_worktree_update},
proto::{self, split_worktree_update, FromProto, ToProto},
AnyProtoClient,
};
pub use settings::WorktreeId;
@ -283,13 +283,13 @@ impl RepositoryEntry {
current_new_entry = new_statuses.next();
}
Ordering::Greater => {
removed_statuses.push(old_entry.repo_path.to_proto());
removed_statuses.push(old_entry.repo_path.as_ref().to_proto());
current_old_entry = old_statuses.next();
}
}
}
(None, Some(old_entry)) => {
removed_statuses.push(old_entry.repo_path.to_proto());
removed_statuses.push(old_entry.repo_path.as_ref().to_proto());
current_old_entry = old_statuses.next();
}
(Some(new_entry), None) => {
@ -308,7 +308,7 @@ impl RepositoryEntry {
current_merge_conflicts: self
.current_merge_conflicts
.iter()
.map(RepoPath::to_proto)
.map(|path| path.as_ref().to_proto())
.collect(),
}
}
@ -700,7 +700,7 @@ impl Worktree {
let snapshot = Snapshot::new(
worktree.id,
worktree.root_name,
Arc::from(PathBuf::from(worktree.abs_path)),
Arc::<Path>::from_proto(worktree.abs_path),
);
let background_snapshot = Arc::new(Mutex::new((snapshot.clone(), Vec::new())));
@ -849,7 +849,7 @@ impl Worktree {
id: self.id().to_proto(),
root_name: self.root_name().to_string(),
visible: self.is_visible(),
abs_path: self.abs_path().as_os_str().to_string_lossy().into(),
abs_path: self.abs_path().to_proto(),
}
}
@ -1007,7 +1007,7 @@ impl Worktree {
is_directory: bool,
cx: &Context<Worktree>,
) -> Task<Result<CreatedEntry>> {
let path = path.into();
let path: Arc<Path> = path.into();
let worktree_id = self.id();
match self {
Worktree::Local(this) => this.create_entry(path, is_directory, cx),
@ -1016,7 +1016,7 @@ impl Worktree {
let request = this.client.request(proto::CreateProjectEntry {
worktree_id: worktree_id.to_proto(),
project_id,
path: path.to_string_lossy().into(),
path: path.as_ref().to_proto(),
is_directory,
});
cx.spawn(move |this, mut cx| async move {
@ -1101,21 +1101,19 @@ impl Worktree {
new_path: impl Into<Arc<Path>>,
cx: &Context<Self>,
) -> Task<Result<Option<Entry>>> {
let new_path = new_path.into();
let new_path: Arc<Path> = new_path.into();
match self {
Worktree::Local(this) => {
this.copy_entry(entry_id, relative_worktree_source_path, new_path, cx)
}
Worktree::Remote(this) => {
let relative_worktree_source_path =
relative_worktree_source_path.map(|relative_worktree_source_path| {
relative_worktree_source_path.to_string_lossy().into()
});
let relative_worktree_source_path = relative_worktree_source_path
.map(|relative_worktree_source_path| relative_worktree_source_path.to_proto());
let response = this.client.request(proto::CopyProjectEntry {
project_id: this.project_id,
entry_id: entry_id.to_proto(),
relative_worktree_source_path,
new_path: new_path.to_string_lossy().into(),
new_path: new_path.to_proto(),
});
cx.spawn(move |this, mut cx| async move {
let response = response.await?;
@ -1214,7 +1212,11 @@ impl Worktree {
let (scan_id, entry) = this.update(&mut cx, |this, cx| {
(
this.scan_id(),
this.create_entry(PathBuf::from(request.path), request.is_directory, cx),
this.create_entry(
Arc::<Path>::from_proto(request.path),
request.is_directory,
cx,
),
)
})?;
Ok(proto::ProjectEntryResponse {
@ -1288,7 +1290,7 @@ impl Worktree {
this.scan_id(),
this.rename_entry(
ProjectEntryId::from_proto(request.entry_id),
PathBuf::from(request.new_path),
Arc::<Path>::from_proto(request.new_path),
cx,
),
)
@ -1308,14 +1310,15 @@ impl Worktree {
mut cx: AsyncApp,
) -> Result<proto::ProjectEntryResponse> {
let (scan_id, task) = this.update(&mut cx, |this, cx| {
let relative_worktree_source_path =
request.relative_worktree_source_path.map(PathBuf::from);
let relative_worktree_source_path = request
.relative_worktree_source_path
.map(PathBuf::from_proto);
(
this.scan_id(),
this.copy_entry(
ProjectEntryId::from_proto(request.entry_id),
relative_worktree_source_path,
PathBuf::from(request.new_path),
PathBuf::from_proto(request.new_path),
cx,
),
)
@ -2368,11 +2371,11 @@ impl RemoteWorktree {
new_path: impl Into<Arc<Path>>,
cx: &Context<Worktree>,
) -> Task<Result<CreatedEntry>> {
let new_path = new_path.into();
let new_path: Arc<Path> = new_path.into();
let response = self.client.request(proto::RenameProjectEntry {
project_id: self.project_id,
entry_id: entry_id.to_proto(),
new_path: new_path.to_string_lossy().into(),
new_path: new_path.as_ref().to_proto(),
});
cx.spawn(move |this, mut cx| async move {
let response = response.await?;
@ -2454,7 +2457,7 @@ impl Snapshot {
proto::UpdateWorktree {
project_id,
worktree_id,
abs_path: self.abs_path().to_string_lossy().into(),
abs_path: self.abs_path().to_proto(),
root_name: self.root_name().to_string(),
updated_entries,
removed_entries: Vec::new(),
@ -2555,7 +2558,7 @@ impl Snapshot {
update.removed_entries.len()
);
self.update_abs_path(
SanitizedPath::from(PathBuf::from(update.abs_path)),
SanitizedPath::from(PathBuf::from_proto(update.abs_path)),
update.root_name,
);
@ -2617,7 +2620,7 @@ impl Snapshot {
let edits = repository
.removed_statuses
.into_iter()
.map(|path| Edit::Remove(PathKey(Path::new(&path).into())))
.map(|path| Edit::Remove(PathKey(FromProto::from_proto(path))))
.chain(repository.updated_statuses.into_iter().filter_map(
|updated_status| {
Some(Edit::Insert(updated_status.try_into().log_err()?))
@ -2952,7 +2955,7 @@ impl LocalSnapshot {
proto::UpdateWorktree {
project_id,
worktree_id,
abs_path: self.abs_path().to_string_lossy().into(),
abs_path: self.abs_path().to_proto(),
root_name: self.root_name().to_string(),
updated_entries,
removed_entries,
@ -3635,7 +3638,7 @@ impl language::File for File {
rpc::proto::File {
worktree_id: self.worktree.read(cx).id().to_proto(),
entry_id: self.entry_id.map(|id| id.to_proto()),
path: self.path.to_string_lossy().into(),
path: self.path.as_ref().to_proto(),
mtime: self.disk_state.mtime().map(|time| time.into()),
is_deleted: self.disk_state == DiskState::Deleted,
}
@ -3716,7 +3719,7 @@ impl File {
Ok(Self {
worktree,
path: Path::new(&proto.path).into(),
path: Arc::<Path>::from_proto(proto.path),
disk_state,
entry_id: proto.entry_id.map(ProjectEntryId::from_proto),
is_local: false,
@ -3835,8 +3838,9 @@ impl StatusEntry {
index_status
}),
};
proto::StatusEntry {
repo_path: self.repo_path.to_proto(),
repo_path: self.repo_path.as_ref().to_proto(),
simple_status,
status: Some(status_to_proto(self.status)),
}
@ -3847,7 +3851,7 @@ impl TryFrom<proto::StatusEntry> for StatusEntry {
type Error = anyhow::Error;
fn try_from(value: proto::StatusEntry) -> Result<Self, Self::Error> {
let repo_path = RepoPath(Path::new(&value.repo_path).into());
let repo_path = RepoPath(Arc::<Path>::from_proto(value.repo_path));
let status = status_from_proto(value.simple_status, value.status)?;
Ok(Self { repo_path, status })
}
@ -6231,7 +6235,7 @@ impl<'a> From<&'a Entry> for proto::Entry {
Self {
id: entry.id.to_proto(),
is_dir: entry.is_dir(),
path: entry.path.to_string_lossy().into(),
path: entry.path.as_ref().to_proto(),
inode: entry.inode,
mtime: entry.mtime.map(|time| time.into()),
is_ignored: entry.is_ignored,
@ -6241,7 +6245,7 @@ impl<'a> From<&'a Entry> for proto::Entry {
canonical_path: entry
.canonical_path
.as_ref()
.map(|path| path.to_string_lossy().to_string()),
.map(|path| path.as_ref().to_proto()),
}
}
}
@ -6257,20 +6261,22 @@ impl<'a> TryFrom<(&'a CharBag, &PathMatcher, proto::Entry)> for Entry {
} else {
EntryKind::File
};
let path: Arc<Path> = PathBuf::from(entry.path).into();
let path = Arc::<Path>::from_proto(entry.path);
let char_bag = char_bag_for_path(*root_char_bag, &path);
let is_always_included = always_included.is_match(path.as_ref());
Ok(Entry {
id: ProjectEntryId::from_proto(entry.id),
kind,
path: path.clone(),
path,
inode: entry.inode,
mtime: entry.mtime.map(|time| time.into()),
size: entry.size.unwrap_or(0),
canonical_path: entry
.canonical_path
.map(|path_string| Box::from(Path::new(&path_string))),
.map(|path_string| Box::from(PathBuf::from_proto(path_string))),
is_ignored: entry.is_ignored,
is_always_included: always_included.is_match(path.as_ref()),
is_always_included,
is_external: entry.is_external,
is_private: false,
char_bag,