Add support for git branches on remote projects (#19755)
Release Notes: - Fixed a bug where the branch switcher could not be used remotely.
This commit is contained in:
parent
5506669b06
commit
c69da2df70
25 changed files with 993 additions and 127 deletions
|
@ -308,6 +308,8 @@ impl Server {
|
|||
.add_request_handler(forward_read_only_project_request::<proto::InlayHints>)
|
||||
.add_request_handler(forward_read_only_project_request::<proto::ResolveInlayHint>)
|
||||
.add_request_handler(forward_read_only_project_request::<proto::OpenBufferByPath>)
|
||||
.add_request_handler(forward_read_only_project_request::<proto::GitBranches>)
|
||||
.add_request_handler(forward_mutating_project_request::<proto::UpdateGitBranch>)
|
||||
.add_request_handler(forward_mutating_project_request::<proto::GetCompletions>)
|
||||
.add_request_handler(
|
||||
forward_mutating_project_request::<proto::ApplyCompletionAdditionalEdits>,
|
||||
|
|
|
@ -6575,3 +6575,95 @@ async fn test_context_collaboration_with_reconnect(
|
|||
assert!(context.buffer().read(cx).read_only());
|
||||
});
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_remote_git_branches(
|
||||
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);
|
||||
|
||||
client_a
|
||||
.fs()
|
||||
.insert_tree("/project", serde_json::json!({ ".git":{} }))
|
||||
.await;
|
||||
let branches = ["main", "dev", "feature-1"];
|
||||
client_a
|
||||
.fs()
|
||||
.insert_branches(Path::new("/project/.git"), &branches);
|
||||
|
||||
let (project_a, worktree_id) = client_a.build_local_project("/project", cx_a).await;
|
||||
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;
|
||||
|
||||
let root_path = ProjectPath::root_path(worktree_id);
|
||||
// Client A sees that a guest has joined.
|
||||
executor.run_until_parked();
|
||||
|
||||
let branches_b = cx_b
|
||||
.update(|cx| project_b.update(cx, |project, cx| project.branches(root_path.clone(), cx)))
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let new_branch = branches[2];
|
||||
|
||||
let branches_b = branches_b
|
||||
.into_iter()
|
||||
.map(|branch| branch.name)
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
assert_eq!(&branches_b, &branches);
|
||||
|
||||
cx_b.update(|cx| {
|
||||
project_b.update(cx, |project, cx| {
|
||||
project.update_or_create_branch(root_path.clone(), new_branch.to_string(), cx)
|
||||
})
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
executor.run_until_parked();
|
||||
|
||||
let host_branch = cx_a.update(|cx| {
|
||||
project_a.update(cx, |project, cx| {
|
||||
project.worktree_store().update(cx, |worktree_store, cx| {
|
||||
worktree_store
|
||||
.current_branch(root_path.clone(), cx)
|
||||
.unwrap()
|
||||
})
|
||||
})
|
||||
});
|
||||
|
||||
assert_eq!(host_branch.as_ref(), branches[2]);
|
||||
|
||||
// Also try creating a new branch
|
||||
cx_b.update(|cx| {
|
||||
project_b.update(cx, |project, cx| {
|
||||
project.update_or_create_branch(root_path.clone(), "totally-new-branch".to_string(), cx)
|
||||
})
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
executor.run_until_parked();
|
||||
|
||||
let host_branch = cx_a.update(|cx| {
|
||||
project_a.update(cx, |project, cx| {
|
||||
project.worktree_store().update(cx, |worktree_store, cx| {
|
||||
worktree_store.current_branch(root_path, cx).unwrap()
|
||||
})
|
||||
})
|
||||
});
|
||||
|
||||
assert_eq!(host_branch.as_ref(), "totally-new-branch");
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use crate::tests::TestServer;
|
||||
use call::ActiveCall;
|
||||
use fs::{FakeFs, Fs as _};
|
||||
use gpui::{Context as _, TestAppContext};
|
||||
use gpui::{BackgroundExecutor, Context as _, TestAppContext};
|
||||
use http_client::BlockedHttpClient;
|
||||
use language::{language_settings::language_settings, LanguageRegistry};
|
||||
use node_runtime::NodeRuntime;
|
||||
|
@ -174,3 +174,133 @@ async fn test_sharing_an_ssh_remote_project(
|
|||
);
|
||||
});
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_ssh_collaboration_git_branches(
|
||||
executor: BackgroundExecutor,
|
||||
cx_a: &mut TestAppContext,
|
||||
cx_b: &mut TestAppContext,
|
||||
server_cx: &mut TestAppContext,
|
||||
) {
|
||||
cx_a.set_name("a");
|
||||
cx_b.set_name("b");
|
||||
server_cx.set_name("server");
|
||||
|
||||
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;
|
||||
|
||||
// Set up project on remote FS
|
||||
let (opts, server_ssh) = SshRemoteClient::fake_server(cx_a, server_cx);
|
||||
let remote_fs = FakeFs::new(server_cx.executor());
|
||||
remote_fs
|
||||
.insert_tree("/project", serde_json::json!({ ".git":{} }))
|
||||
.await;
|
||||
|
||||
let branches = ["main", "dev", "feature-1"];
|
||||
remote_fs.insert_branches(Path::new("/project/.git"), &branches);
|
||||
|
||||
// User A connects to the remote project via SSH.
|
||||
server_cx.update(HeadlessProject::init);
|
||||
let remote_http_client = Arc::new(BlockedHttpClient);
|
||||
let node = NodeRuntime::unavailable();
|
||||
let languages = Arc::new(LanguageRegistry::new(server_cx.executor()));
|
||||
let headless_project = server_cx.new_model(|cx| {
|
||||
client::init_settings(cx);
|
||||
HeadlessProject::new(
|
||||
HeadlessAppState {
|
||||
session: server_ssh,
|
||||
fs: remote_fs.clone(),
|
||||
http_client: remote_http_client,
|
||||
node_runtime: node,
|
||||
languages,
|
||||
},
|
||||
cx,
|
||||
)
|
||||
});
|
||||
|
||||
let client_ssh = SshRemoteClient::fake_client(opts, cx_a).await;
|
||||
let (project_a, worktree_id) = client_a
|
||||
.build_ssh_project("/project", client_ssh, cx_a)
|
||||
.await;
|
||||
|
||||
// While the SSH worktree is being scanned, user A shares the remote project.
|
||||
let active_call_a = cx_a.read(ActiveCall::global);
|
||||
let project_id = active_call_a
|
||||
.update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
// User B joins the project.
|
||||
let project_b = client_b.join_remote_project(project_id, cx_b).await;
|
||||
|
||||
// Give client A sometime to see that B has joined, and that the headless server
|
||||
// has some git repositories
|
||||
executor.run_until_parked();
|
||||
|
||||
let root_path = ProjectPath::root_path(worktree_id);
|
||||
|
||||
let branches_b = cx_b
|
||||
.update(|cx| project_b.update(cx, |project, cx| project.branches(root_path.clone(), cx)))
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let new_branch = branches[2];
|
||||
|
||||
let branches_b = branches_b
|
||||
.into_iter()
|
||||
.map(|branch| branch.name)
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
assert_eq!(&branches_b, &branches);
|
||||
|
||||
cx_b.update(|cx| {
|
||||
project_b.update(cx, |project, cx| {
|
||||
project.update_or_create_branch(root_path.clone(), new_branch.to_string(), cx)
|
||||
})
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
executor.run_until_parked();
|
||||
|
||||
let server_branch = server_cx.update(|cx| {
|
||||
headless_project.update(cx, |headless_project, cx| {
|
||||
headless_project
|
||||
.worktree_store
|
||||
.update(cx, |worktree_store, cx| {
|
||||
worktree_store
|
||||
.current_branch(root_path.clone(), cx)
|
||||
.unwrap()
|
||||
})
|
||||
})
|
||||
});
|
||||
|
||||
assert_eq!(server_branch.as_ref(), branches[2]);
|
||||
|
||||
// Also try creating a new branch
|
||||
cx_b.update(|cx| {
|
||||
project_b.update(cx, |project, cx| {
|
||||
project.update_or_create_branch(root_path.clone(), "totally-new-branch".to_string(), cx)
|
||||
})
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
executor.run_until_parked();
|
||||
|
||||
let server_branch = server_cx.update(|cx| {
|
||||
headless_project.update(cx, |headless_project, cx| {
|
||||
headless_project
|
||||
.worktree_store
|
||||
.update(cx, |worktree_store, cx| {
|
||||
worktree_store.current_branch(root_path, cx).unwrap()
|
||||
})
|
||||
})
|
||||
});
|
||||
|
||||
assert_eq!(server_branch.as_ref(), "totally-new-branch");
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue