agent: Support remote archive thread git operations (#54001)
### Summary This PR implements remote support for git store operations that the sidebar's archive thread checkpoint/restore featured relied on. This was the second to last blocker for remote usage of this feature. I also made a shared backend between `update_ref` and `delete_ref` called `edit_ref` because they run the same git command and this allowed for some code unification. #### Remote Git Operations - `Repository::update_ref` - `Repository::delete_ref` - `Repository::repair_worktrees` - `Repository::create_archive_checkpoint` - `Repository::restore_archive_checkpoint` #### Follow up `agent_ui::thread_worktree_archive::find_or_create_repository` needs to be made aware of the remote machine that the repository it's searching for is on. Once that is completed, we can get the correct repo when archiving a remote thread and the flow should work without any problems. Self-Review Checklist: - [x] I've reviewed my own diff for quality, security, and reliability - [x] Unsafe blocks (if any) have justifying comments - [x] The content is consistent with the [UI/UX checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist) - [x] Tests cover the new/changed behavior - [x] Performance impact has been considered and is acceptable Release Notes: - N/A
This commit is contained in:
1 parent
e2780f6fbd
commit
d60b55675b
8 files changed
+348
-57
No files matched your search
@@ -439,6 +439,10 @@ impl Server {
|
||||
.add_request_handler(forward_mutating_project_request::<proto::GitCreateWorktree>)
|
||||
.add_request_handler(disallow_guest_request::<proto::GitRemoveWorktree>)
|
||||
.add_request_handler(disallow_guest_request::<proto::GitRenameWorktree>)
|
||||
.add_request_handler(forward_mutating_project_request::<proto::GitEditRef>)
|
||||
.add_request_handler(forward_mutating_project_request::<proto::GitRepairWorktrees>)
|
||||
.add_request_handler(disallow_guest_request::<proto::GitCreateArchiveCheckpoint>)
|
||||
.add_request_handler(disallow_guest_request::<proto::GitRestoreArchiveCheckpoint>)
|
||||
.add_request_handler(forward_mutating_project_request::<proto::CheckForPushedCommits>)
|
||||
.add_request_handler(forward_mutating_project_request::<proto::ToggleLspLogs>)
|
||||
.add_message_handler(broadcast_project_message_from_host::<proto::LanguageServerLog>)
|
||||
|
||||
@@ -10,8 +10,8 @@ use git::{
|
||||
repository::{
|
||||
AskPassDelegate, Branch, CommitDataReader, CommitDetails, CommitOptions,
|
||||
CreateWorktreeTarget, FetchOptions, GRAPH_CHUNK_SIZE, GitRepository,
|
||||
GitRepositoryCheckpoint, InitialGraphCommitData, LogOrder, LogSource, PushOptions, Remote,
|
||||
RepoPath, ResetMode, SearchCommitArgs, Worktree,
|
||||
GitRepositoryCheckpoint, InitialGraphCommitData, LogOrder, LogSource, PushOptions, RefEdit,
|
||||
Remote, RepoPath, ResetMode, SearchCommitArgs, Worktree,
|
||||
},
|
||||
stash::GitStash,
|
||||
status::{
|
||||
@@ -109,6 +109,20 @@ impl FakeGitRepository {
|
||||
.boxed()
|
||||
}
|
||||
|
||||
fn edit_ref(&self, edit: RefEdit) -> BoxFuture<'_, Result<()>> {
|
||||
self.with_state_async(true, move |state| {
|
||||
match edit {
|
||||
RefEdit::Update { ref_name, commit } => {
|
||||
state.refs.insert(ref_name, commit);
|
||||
}
|
||||
RefEdit::Delete { ref_name } => {
|
||||
state.refs.remove(&ref_name);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
||||
/// Scans `.git/worktrees/*/gitdir` to find the admin entry directory for a
|
||||
/// worktree at the given checkout path. Used when the working tree directory
|
||||
/// has already been deleted and we can't read its `.git` pointer file.
|
||||
@@ -1437,17 +1451,11 @@ impl GitRepository for FakeGitRepository {
|
||||
}
|
||||
|
||||
fn update_ref(&self, ref_name: String, commit: String) -> BoxFuture<'_, Result<()>> {
|
||||
self.with_state_async(true, move |state| {
|
||||
state.refs.insert(ref_name, commit);
|
||||
Ok(())
|
||||
})
|
||||
self.edit_ref(RefEdit::Update { ref_name, commit })
|
||||
}
|
||||
|
||||
fn delete_ref(&self, ref_name: String) -> BoxFuture<'_, Result<()>> {
|
||||
self.with_state_async(true, move |state| {
|
||||
state.refs.remove(&ref_name);
|
||||
Ok(())
|
||||
})
|
||||
self.edit_ref(RefEdit::Delete { ref_name })
|
||||
}
|
||||
|
||||
fn repair_worktrees(&self) -> BoxFuture<'_, Result<()>> {
|
||||
|
||||
@@ -1043,6 +1043,25 @@ pub struct RealGitRepository {
|
||||
is_trusted: Arc<AtomicBool>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum RefEdit {
|
||||
Update { ref_name: String, commit: String },
|
||||
Delete { ref_name: String },
|
||||
}
|
||||
|
||||
impl RefEdit {
|
||||
fn into_args(self) -> Vec<OsString> {
|
||||
match self {
|
||||
Self::Update { ref_name, commit } => {
|
||||
vec!["update-ref".into(), ref_name.into(), commit.into()]
|
||||
}
|
||||
Self::Delete { ref_name } => {
|
||||
vec!["update-ref".into(), "-d".into(), ref_name.into()]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl RealGitRepository {
|
||||
pub fn new(
|
||||
dotgit_path: &Path,
|
||||
@@ -1089,6 +1108,17 @@ impl RealGitRepository {
|
||||
))
|
||||
}
|
||||
|
||||
fn edit_ref(&self, edit: RefEdit) -> BoxFuture<'_, Result<()>> {
|
||||
let git_binary = self.git_binary();
|
||||
self.executor
|
||||
.spawn(async move {
|
||||
let args = edit.into_args();
|
||||
git_binary?.run(&args).await?;
|
||||
Ok(())
|
||||
})
|
||||
.boxed()
|
||||
}
|
||||
|
||||
async fn any_git_binary_help_output(&self) -> SharedString {
|
||||
if let Some(output) = self.any_git_binary_help_output.lock().clone() {
|
||||
return output;
|
||||
@@ -2316,25 +2346,11 @@ impl GitRepository for RealGitRepository {
|
||||
}
|
||||
|
||||
fn update_ref(&self, ref_name: String, commit: String) -> BoxFuture<'_, Result<()>> {
|
||||
let git_binary = self.git_binary();
|
||||
self.executor
|
||||
.spawn(async move {
|
||||
let args: Vec<OsString> = vec!["update-ref".into(), ref_name.into(), commit.into()];
|
||||
git_binary?.run(&args).await?;
|
||||
Ok(())
|
||||
})
|
||||
.boxed()
|
||||
self.edit_ref(RefEdit::Update { ref_name, commit })
|
||||
}
|
||||
|
||||
fn delete_ref(&self, ref_name: String) -> BoxFuture<'_, Result<()>> {
|
||||
let git_binary = self.git_binary();
|
||||
self.executor
|
||||
.spawn(async move {
|
||||
let args: Vec<OsString> = vec!["update-ref".into(), "-d".into(), ref_name.into()];
|
||||
git_binary?.run(&args).await?;
|
||||
Ok(())
|
||||
})
|
||||
.boxed()
|
||||
self.edit_ref(RefEdit::Delete { ref_name })
|
||||
}
|
||||
|
||||
fn repair_worktrees(&self) -> BoxFuture<'_, Result<()>> {
|
||||
|
||||
+147
-30
@@ -563,7 +563,9 @@ impl GitStore {
|
||||
client.add_entity_request_handler(Self::handle_reset);
|
||||
client.add_entity_request_handler(Self::handle_show);
|
||||
client.add_entity_request_handler(Self::handle_create_checkpoint);
|
||||
client.add_entity_request_handler(Self::handle_create_archive_checkpoint);
|
||||
client.add_entity_request_handler(Self::handle_restore_checkpoint);
|
||||
client.add_entity_request_handler(Self::handle_restore_archive_checkpoint);
|
||||
client.add_entity_request_handler(Self::handle_compare_checkpoints);
|
||||
client.add_entity_request_handler(Self::handle_diff_checkpoints);
|
||||
client.add_entity_request_handler(Self::handle_load_commit_diff);
|
||||
@@ -589,6 +591,8 @@ impl GitStore {
|
||||
client.add_entity_request_handler(Self::handle_remove_worktree);
|
||||
client.add_entity_request_handler(Self::handle_rename_worktree);
|
||||
client.add_entity_request_handler(Self::handle_get_head_sha);
|
||||
client.add_entity_request_handler(Self::handle_edit_ref);
|
||||
client.add_entity_request_handler(Self::handle_repair_worktrees);
|
||||
}
|
||||
|
||||
pub fn is_local(&self) -> bool {
|
||||
@@ -2519,6 +2523,46 @@ impl GitStore {
|
||||
Ok(proto::GitGetHeadShaResponse { sha: head_sha })
|
||||
}
|
||||
|
||||
async fn handle_edit_ref(
|
||||
this: Entity<Self>,
|
||||
envelope: TypedEnvelope<proto::GitEditRef>,
|
||||
mut cx: AsyncApp,
|
||||
) -> Result<proto::Ack> {
|
||||
let repository_id = RepositoryId::from_proto(envelope.payload.repository_id);
|
||||
let repository_handle = Self::repository_for_request(&this, repository_id, &mut cx)?;
|
||||
let ref_name = envelope.payload.ref_name;
|
||||
let commit = match envelope.payload.action {
|
||||
Some(proto::git_edit_ref::Action::UpdateToCommit(sha)) => Some(sha),
|
||||
Some(proto::git_edit_ref::Action::Delete(_)) => None,
|
||||
None => anyhow::bail!("GitEditRef missing action"),
|
||||
};
|
||||
|
||||
repository_handle
|
||||
.update(&mut cx, |repository_handle, _| {
|
||||
repository_handle.edit_ref(ref_name, commit)
|
||||
})
|
||||
.await??;
|
||||
|
||||
Ok(proto::Ack {})
|
||||
}
|
||||
|
||||
async fn handle_repair_worktrees(
|
||||
this: Entity<Self>,
|
||||
envelope: TypedEnvelope<proto::GitRepairWorktrees>,
|
||||
mut cx: AsyncApp,
|
||||
) -> Result<proto::Ack> {
|
||||
let repository_id = RepositoryId::from_proto(envelope.payload.repository_id);
|
||||
let repository_handle = Self::repository_for_request(&this, repository_id, &mut cx)?;
|
||||
|
||||
repository_handle
|
||||
.update(&mut cx, |repository_handle, _| {
|
||||
repository_handle.repair_worktrees()
|
||||
})
|
||||
.await??;
|
||||
|
||||
Ok(proto::Ack {})
|
||||
}
|
||||
|
||||
async fn handle_get_branches(
|
||||
this: Entity<Self>,
|
||||
envelope: TypedEnvelope<proto::GitGetBranches>,
|
||||
@@ -2705,6 +2749,26 @@ impl GitStore {
|
||||
})
|
||||
}
|
||||
|
||||
async fn handle_create_archive_checkpoint(
|
||||
this: Entity<Self>,
|
||||
envelope: TypedEnvelope<proto::GitCreateArchiveCheckpoint>,
|
||||
mut cx: AsyncApp,
|
||||
) -> Result<proto::GitCreateArchiveCheckpointResponse> {
|
||||
let repository_id = RepositoryId::from_proto(envelope.payload.repository_id);
|
||||
let repository_handle = Self::repository_for_request(&this, repository_id, &mut cx)?;
|
||||
|
||||
let (staged_commit_sha, unstaged_commit_sha) = repository_handle
|
||||
.update(&mut cx, |repository, _| {
|
||||
repository.create_archive_checkpoint()
|
||||
})
|
||||
.await??;
|
||||
|
||||
Ok(proto::GitCreateArchiveCheckpointResponse {
|
||||
staged_commit_sha,
|
||||
unstaged_commit_sha,
|
||||
})
|
||||
}
|
||||
|
||||
async fn handle_restore_checkpoint(
|
||||
this: Entity<Self>,
|
||||
envelope: TypedEnvelope<proto::GitRestoreCheckpoint>,
|
||||
@@ -2726,6 +2790,25 @@ impl GitStore {
|
||||
Ok(proto::Ack {})
|
||||
}
|
||||
|
||||
async fn handle_restore_archive_checkpoint(
|
||||
this: Entity<Self>,
|
||||
envelope: TypedEnvelope<proto::GitRestoreArchiveCheckpoint>,
|
||||
mut cx: AsyncApp,
|
||||
) -> Result<proto::Ack> {
|
||||
let repository_id = RepositoryId::from_proto(envelope.payload.repository_id);
|
||||
let repository_handle = Self::repository_for_request(&this, repository_id, &mut cx)?;
|
||||
let staged_commit_sha = envelope.payload.staged_commit_sha;
|
||||
let unstaged_commit_sha = envelope.payload.unstaged_commit_sha;
|
||||
|
||||
repository_handle
|
||||
.update(&mut cx, |repository, _| {
|
||||
repository.restore_archive_checkpoint(staged_commit_sha, unstaged_commit_sha)
|
||||
})
|
||||
.await??;
|
||||
|
||||
Ok(proto::Ack {})
|
||||
}
|
||||
|
||||
async fn handle_compare_checkpoints(
|
||||
this: Entity<Self>,
|
||||
envelope: TypedEnvelope<proto::GitCompareCheckpoints>,
|
||||
@@ -6147,59 +6230,86 @@ impl Repository {
|
||||
})
|
||||
}
|
||||
|
||||
fn edit_ref(
|
||||
&mut self,
|
||||
ref_name: String,
|
||||
commit: Option<String>,
|
||||
) -> oneshot::Receiver<Result<()>> {
|
||||
let id = self.id;
|
||||
self.send_job(None, move |repo, _cx| async move {
|
||||
match repo {
|
||||
RepositoryState::Local(LocalRepositoryState { backend, .. }) => match commit {
|
||||
Some(commit) => backend.update_ref(ref_name, commit).await,
|
||||
None => backend.delete_ref(ref_name).await,
|
||||
},
|
||||
RepositoryState::Remote(RemoteRepositoryState { project_id, client }) => {
|
||||
let action = match commit {
|
||||
Some(sha) => proto::git_edit_ref::Action::UpdateToCommit(sha),
|
||||
None => {
|
||||
proto::git_edit_ref::Action::Delete(proto::git_edit_ref::DeleteRef {})
|
||||
}
|
||||
};
|
||||
client
|
||||
.request(proto::GitEditRef {
|
||||
project_id: project_id.0,
|
||||
repository_id: id.to_proto(),
|
||||
ref_name,
|
||||
action: Some(action),
|
||||
})
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub fn update_ref(
|
||||
&mut self,
|
||||
ref_name: String,
|
||||
commit: String,
|
||||
) -> oneshot::Receiver<Result<()>> {
|
||||
self.send_job(None, move |repo, _cx| async move {
|
||||
match repo {
|
||||
RepositoryState::Local(LocalRepositoryState { backend, .. }) => {
|
||||
backend.update_ref(ref_name, commit).await
|
||||
}
|
||||
RepositoryState::Remote(_) => {
|
||||
anyhow::bail!("update_ref is not supported for remote repositories")
|
||||
}
|
||||
}
|
||||
})
|
||||
self.edit_ref(ref_name, Some(commit))
|
||||
}
|
||||
|
||||
pub fn delete_ref(&mut self, ref_name: String) -> oneshot::Receiver<Result<()>> {
|
||||
self.send_job(None, move |repo, _cx| async move {
|
||||
match repo {
|
||||
RepositoryState::Local(LocalRepositoryState { backend, .. }) => {
|
||||
backend.delete_ref(ref_name).await
|
||||
}
|
||||
RepositoryState::Remote(_) => {
|
||||
anyhow::bail!("delete_ref is not supported for remote repositories")
|
||||
}
|
||||
}
|
||||
})
|
||||
self.edit_ref(ref_name, None)
|
||||
}
|
||||
|
||||
pub fn repair_worktrees(&mut self) -> oneshot::Receiver<Result<()>> {
|
||||
let id = self.id;
|
||||
self.send_job(None, move |repo, _cx| async move {
|
||||
match repo {
|
||||
RepositoryState::Local(LocalRepositoryState { backend, .. }) => {
|
||||
backend.repair_worktrees().await
|
||||
}
|
||||
RepositoryState::Remote(_) => {
|
||||
anyhow::bail!("repair_worktrees is not supported for remote repositories")
|
||||
RepositoryState::Remote(RemoteRepositoryState { project_id, client }) => {
|
||||
client
|
||||
.request(proto::GitRepairWorktrees {
|
||||
project_id: project_id.0,
|
||||
repository_id: id.to_proto(),
|
||||
})
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub fn create_archive_checkpoint(&mut self) -> oneshot::Receiver<Result<(String, String)>> {
|
||||
let id = self.id;
|
||||
self.send_job(None, move |repo, _cx| async move {
|
||||
match repo {
|
||||
RepositoryState::Local(LocalRepositoryState { backend, .. }) => {
|
||||
backend.create_archive_checkpoint().await
|
||||
}
|
||||
RepositoryState::Remote(_) => {
|
||||
anyhow::bail!(
|
||||
"create_archive_checkpoint is not supported for remote repositories"
|
||||
)
|
||||
RepositoryState::Remote(RemoteRepositoryState { project_id, client }) => {
|
||||
let response = client
|
||||
.request(proto::GitCreateArchiveCheckpoint {
|
||||
project_id: project_id.0,
|
||||
repository_id: id.to_proto(),
|
||||
})
|
||||
.await?;
|
||||
Ok((response.staged_commit_sha, response.unstaged_commit_sha))
|
||||
}
|
||||
}
|
||||
})
|
||||
@@ -6210,6 +6320,7 @@ impl Repository {
|
||||
staged_sha: String,
|
||||
unstaged_sha: String,
|
||||
) -> oneshot::Receiver<Result<()>> {
|
||||
let id = self.id;
|
||||
self.send_job(None, move |repo, _cx| async move {
|
||||
match repo {
|
||||
RepositoryState::Local(LocalRepositoryState { backend, .. }) => {
|
||||
@@ -6217,10 +6328,16 @@ impl Repository {
|
||||
.restore_archive_checkpoint(staged_sha, unstaged_sha)
|
||||
.await
|
||||
}
|
||||
RepositoryState::Remote(_) => {
|
||||
anyhow::bail!(
|
||||
"restore_archive_checkpoint is not supported for remote repositories"
|
||||
)
|
||||
RepositoryState::Remote(RemoteRepositoryState { project_id, client }) => {
|
||||
client
|
||||
.request(proto::GitRestoreArchiveCheckpoint {
|
||||
project_id: project_id.0,
|
||||
repository_id: id.to_proto(),
|
||||
staged_commit_sha: staged_sha,
|
||||
unstaged_commit_sha: unstaged_sha,
|
||||
})
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
@@ -577,6 +577,22 @@ message GitGetHeadShaResponse {
|
||||
optional string sha = 1;
|
||||
}
|
||||
|
||||
message GitEditRef {
|
||||
uint64 project_id = 1;
|
||||
uint64 repository_id = 2;
|
||||
string ref_name = 3;
|
||||
oneof action {
|
||||
string update_to_commit = 4;
|
||||
DeleteRef delete = 5;
|
||||
}
|
||||
message DeleteRef {}
|
||||
}
|
||||
|
||||
message GitRepairWorktrees {
|
||||
uint64 project_id = 1;
|
||||
uint64 repository_id = 2;
|
||||
}
|
||||
|
||||
message GitWorktreesResponse {
|
||||
repeated Worktree worktrees = 1;
|
||||
}
|
||||
@@ -607,12 +623,29 @@ message GitCreateCheckpointResponse {
|
||||
bytes commit_sha = 1;
|
||||
}
|
||||
|
||||
message GitCreateArchiveCheckpoint {
|
||||
uint64 project_id = 1;
|
||||
uint64 repository_id = 2;
|
||||
}
|
||||
|
||||
message GitCreateArchiveCheckpointResponse {
|
||||
string staged_commit_sha = 1;
|
||||
string unstaged_commit_sha = 2;
|
||||
}
|
||||
|
||||
message GitRestoreCheckpoint {
|
||||
uint64 project_id = 1;
|
||||
uint64 repository_id = 2;
|
||||
bytes commit_sha = 3;
|
||||
}
|
||||
|
||||
message GitRestoreArchiveCheckpoint {
|
||||
uint64 project_id = 1;
|
||||
uint64 repository_id = 2;
|
||||
string staged_commit_sha = 3;
|
||||
string unstaged_commit_sha = 4;
|
||||
}
|
||||
|
||||
message GitCompareCheckpoints {
|
||||
uint64 project_id = 1;
|
||||
uint64 repository_id = 2;
|
||||
|
||||
@@ -476,7 +476,12 @@ message Envelope {
|
||||
GitDiffCheckpoints git_diff_checkpoints = 438;
|
||||
GitDiffCheckpointsResponse git_diff_checkpoints_response = 439;
|
||||
GitGetHeadSha git_get_head_sha = 440;
|
||||
GitGetHeadShaResponse git_get_head_sha_response = 441; // current max
|
||||
GitGetHeadShaResponse git_get_head_sha_response = 441;
|
||||
GitRepairWorktrees git_repair_worktrees = 442;
|
||||
GitEditRef git_edit_ref = 443;
|
||||
GitCreateArchiveCheckpoint git_create_archive_checkpoint = 444;
|
||||
GitCreateArchiveCheckpointResponse git_create_archive_checkpoint_response = 445;
|
||||
GitRestoreArchiveCheckpoint git_restore_archive_checkpoint = 446; // current max
|
||||
}
|
||||
|
||||
reserved 87 to 88;
|
||||
|
||||
@@ -296,7 +296,10 @@ messages!(
|
||||
(GitFileHistoryResponse, Background),
|
||||
(GitCreateCheckpoint, Background),
|
||||
(GitCreateCheckpointResponse, Background),
|
||||
(GitCreateArchiveCheckpoint, Background),
|
||||
(GitCreateArchiveCheckpointResponse, Background),
|
||||
(GitRestoreCheckpoint, Background),
|
||||
(GitRestoreArchiveCheckpoint, Background),
|
||||
(GitCompareCheckpoints, Background),
|
||||
(GitCompareCheckpointsResponse, Background),
|
||||
(GitDiffCheckpoints, Background),
|
||||
@@ -353,6 +356,8 @@ messages!(
|
||||
(GitGetWorktrees, Background),
|
||||
(GitGetHeadSha, Background),
|
||||
(GitGetHeadShaResponse, Background),
|
||||
(GitEditRef, Background),
|
||||
(GitRepairWorktrees, Background),
|
||||
(GitWorktreesResponse, Background),
|
||||
(GitCreateWorktree, Background),
|
||||
(GitRemoveWorktree, Background),
|
||||
@@ -524,7 +529,12 @@ request_messages!(
|
||||
(GitShow, GitCommitDetails),
|
||||
(GitFileHistory, GitFileHistoryResponse),
|
||||
(GitCreateCheckpoint, GitCreateCheckpointResponse),
|
||||
(
|
||||
GitCreateArchiveCheckpoint,
|
||||
GitCreateArchiveCheckpointResponse
|
||||
),
|
||||
(GitRestoreCheckpoint, Ack),
|
||||
(GitRestoreArchiveCheckpoint, Ack),
|
||||
(GitCompareCheckpoints, GitCompareCheckpointsResponse),
|
||||
(GitDiffCheckpoints, GitDiffCheckpointsResponse),
|
||||
(GitReset, Ack),
|
||||
@@ -561,6 +571,8 @@ request_messages!(
|
||||
(RemoteStarted, Ack),
|
||||
(GitGetWorktrees, GitWorktreesResponse),
|
||||
(GitGetHeadSha, GitGetHeadShaResponse),
|
||||
(GitEditRef, Ack),
|
||||
(GitRepairWorktrees, Ack),
|
||||
(GitCreateWorktree, Ack),
|
||||
(GitRemoveWorktree, Ack),
|
||||
(GitRenameWorktree, Ack),
|
||||
@@ -753,6 +765,10 @@ entity_messages!(
|
||||
NewExternalAgentVersionAvailable,
|
||||
GitGetWorktrees,
|
||||
GitGetHeadSha,
|
||||
GitEditRef,
|
||||
GitRepairWorktrees,
|
||||
GitCreateArchiveCheckpoint,
|
||||
GitRestoreArchiveCheckpoint,
|
||||
GitCreateWorktree,
|
||||
GitRemoveWorktree,
|
||||
GitRenameWorktree,
|
||||
|
||||
@@ -1622,6 +1622,98 @@ async fn test_remote_root_repo_common_dir(cx: &mut TestAppContext, server_cx: &m
|
||||
assert_eq!(common_dir, None);
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_remote_archive_git_operations_are_supported(
|
||||
cx: &mut TestAppContext,
|
||||
server_cx: &mut TestAppContext,
|
||||
) {
|
||||
let fs = FakeFs::new(server_cx.executor());
|
||||
fs.insert_tree(
|
||||
"/project",
|
||||
json!({
|
||||
".git": {},
|
||||
"file.txt": "content",
|
||||
}),
|
||||
)
|
||||
.await;
|
||||
fs.set_branch_name(Path::new("/project/.git"), Some("main"));
|
||||
fs.set_head_for_repo(
|
||||
Path::new("/project/.git"),
|
||||
&[("file.txt", "content".into())],
|
||||
"head-sha",
|
||||
);
|
||||
|
||||
let (project, _headless) = init_test(&fs, cx, server_cx).await;
|
||||
project
|
||||
.update(cx, |project, cx| {
|
||||
project.find_or_create_worktree(Path::new("/project"), true, cx)
|
||||
})
|
||||
.await
|
||||
.expect("should open remote worktree");
|
||||
cx.run_until_parked();
|
||||
|
||||
let repository = project.read_with(cx, |project, cx| {
|
||||
project
|
||||
.active_repository(cx)
|
||||
.expect("remote project should have an active repository")
|
||||
});
|
||||
|
||||
let head_sha = cx
|
||||
.update(|cx| repository.update(cx, |repository, _| repository.head_sha()))
|
||||
.await
|
||||
.expect("head_sha request should complete")
|
||||
.expect("head_sha should succeed")
|
||||
.expect("HEAD should exist");
|
||||
|
||||
cx.run_until_parked();
|
||||
|
||||
cx.update(|cx| {
|
||||
repository.update(cx, |repository, _| {
|
||||
repository.update_ref("refs/zed-tests/archive-checkpoint".to_string(), head_sha)
|
||||
})
|
||||
})
|
||||
.await
|
||||
.expect("update_ref request should complete")
|
||||
.expect("update_ref should succeed for remote repository");
|
||||
|
||||
cx.run_until_parked();
|
||||
|
||||
cx.update(|cx| {
|
||||
repository.update(cx, |repository, _| {
|
||||
repository.delete_ref("refs/zed-tests/archive-checkpoint".to_string())
|
||||
})
|
||||
})
|
||||
.await
|
||||
.expect("delete_ref request should complete")
|
||||
.expect("delete_ref should succeed for remote repository");
|
||||
|
||||
cx.run_until_parked();
|
||||
|
||||
cx.update(|cx| repository.update(cx, |repository, _| repository.repair_worktrees()))
|
||||
.await
|
||||
.expect("repair_worktrees request should complete")
|
||||
.expect("repair_worktrees should succeed for remote repository");
|
||||
|
||||
cx.run_until_parked();
|
||||
|
||||
let (staged_commit_sha, unstaged_commit_sha) = cx
|
||||
.update(|cx| repository.update(cx, |repository, _| repository.create_archive_checkpoint()))
|
||||
.await
|
||||
.expect("create_archive_checkpoint request should complete")
|
||||
.expect("create_archive_checkpoint should succeed for remote repository");
|
||||
|
||||
cx.run_until_parked();
|
||||
|
||||
cx.update(|cx| {
|
||||
repository.update(cx, |repository, _| {
|
||||
repository.restore_archive_checkpoint(staged_commit_sha, unstaged_commit_sha)
|
||||
})
|
||||
})
|
||||
.await
|
||||
.expect("restore_archive_checkpoint request should complete")
|
||||
.expect("restore_archive_checkpoint should succeed for remote repository");
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_remote_git_diffs(cx: &mut TestAppContext, server_cx: &mut TestAppContext) {
|
||||
let text_2 = "
|
||||
|
||||
Reference in new issue
Block a user