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
|
@ -288,6 +288,13 @@ impl ProjectPath {
|
|||
path: self.path.to_string_lossy().to_string(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn root_path(worktree_id: WorktreeId) -> Self {
|
||||
Self {
|
||||
worktree_id,
|
||||
path: Path::new("").into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
|
@ -701,7 +708,7 @@ impl Project {
|
|||
|
||||
let ssh_proto = ssh.read(cx).proto_client();
|
||||
let worktree_store =
|
||||
cx.new_model(|_| WorktreeStore::remote(false, ssh_proto.clone(), 0));
|
||||
cx.new_model(|_| WorktreeStore::remote(false, ssh_proto.clone(), SSH_PROJECT_ID));
|
||||
cx.subscribe(&worktree_store, Self::on_worktree_store_event)
|
||||
.detach();
|
||||
|
||||
|
@ -3370,6 +3377,25 @@ impl Project {
|
|||
worktree.get_local_repo(&root_entry)?.repo().clone().into()
|
||||
}
|
||||
|
||||
pub fn branches(
|
||||
&self,
|
||||
project_path: ProjectPath,
|
||||
cx: &AppContext,
|
||||
) -> Task<Result<Vec<git::repository::Branch>>> {
|
||||
self.worktree_store().read(cx).branches(project_path, cx)
|
||||
}
|
||||
|
||||
pub fn update_or_create_branch(
|
||||
&self,
|
||||
repository: ProjectPath,
|
||||
new_branch: String,
|
||||
cx: &AppContext,
|
||||
) -> Task<Result<()>> {
|
||||
self.worktree_store()
|
||||
.read(cx)
|
||||
.update_or_create_branch(repository, new_branch, cx)
|
||||
}
|
||||
|
||||
pub fn blame_buffer(
|
||||
&self,
|
||||
buffer: &Model<Buffer>,
|
||||
|
|
|
@ -73,6 +73,8 @@ impl WorktreeStore {
|
|||
client.add_model_request_handler(Self::handle_copy_project_entry);
|
||||
client.add_model_request_handler(Self::handle_delete_project_entry);
|
||||
client.add_model_request_handler(Self::handle_expand_project_entry);
|
||||
client.add_model_request_handler(Self::handle_git_branches);
|
||||
client.add_model_request_handler(Self::handle_update_branch);
|
||||
}
|
||||
|
||||
pub fn local(retain_worktrees: bool, fs: Arc<dyn Fs>) -> Self {
|
||||
|
@ -127,6 +129,13 @@ impl WorktreeStore {
|
|||
.find(|worktree| worktree.read(cx).id() == id)
|
||||
}
|
||||
|
||||
pub fn current_branch(&self, repository: ProjectPath, cx: &AppContext) -> Option<Arc<str>> {
|
||||
self.worktree_for_id(repository.worktree_id, cx)?
|
||||
.read(cx)
|
||||
.git_entry(repository.path)?
|
||||
.branch()
|
||||
}
|
||||
|
||||
pub fn worktree_for_entry(
|
||||
&self,
|
||||
entry_id: ProjectEntryId,
|
||||
|
@ -836,6 +845,131 @@ impl WorktreeStore {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
pub fn branches(
|
||||
&self,
|
||||
project_path: ProjectPath,
|
||||
cx: &AppContext,
|
||||
) -> Task<Result<Vec<git::repository::Branch>>> {
|
||||
let Some(worktree) = self.worktree_for_id(project_path.worktree_id, cx) else {
|
||||
return Task::ready(Err(anyhow!("No worktree found for ProjectPath")));
|
||||
};
|
||||
|
||||
match worktree.read(cx) {
|
||||
Worktree::Local(local_worktree) => {
|
||||
let branches = util::maybe!({
|
||||
let worktree_error = |error| {
|
||||
format!(
|
||||
"{} for worktree {}",
|
||||
error,
|
||||
local_worktree.abs_path().to_string_lossy()
|
||||
)
|
||||
};
|
||||
|
||||
let entry = local_worktree
|
||||
.git_entry(project_path.path)
|
||||
.with_context(|| worktree_error("No git entry found"))?;
|
||||
|
||||
let repo = local_worktree
|
||||
.get_local_repo(&entry)
|
||||
.with_context(|| worktree_error("No repository found"))?
|
||||
.repo()
|
||||
.clone();
|
||||
|
||||
repo.branches()
|
||||
});
|
||||
|
||||
Task::ready(branches)
|
||||
}
|
||||
Worktree::Remote(remote_worktree) => {
|
||||
let request = remote_worktree.client().request(proto::GitBranches {
|
||||
project_id: remote_worktree.project_id(),
|
||||
repository: Some(proto::ProjectPath {
|
||||
worktree_id: project_path.worktree_id.to_proto(),
|
||||
path: project_path.path.to_string_lossy().to_string(), // Root path
|
||||
}),
|
||||
});
|
||||
|
||||
cx.background_executor().spawn(async move {
|
||||
let response = request.await?;
|
||||
|
||||
let branches = response
|
||||
.branches
|
||||
.into_iter()
|
||||
.map(|proto_branch| git::repository::Branch {
|
||||
is_head: proto_branch.is_head,
|
||||
name: proto_branch.name.into(),
|
||||
unix_timestamp: proto_branch
|
||||
.unix_timestamp
|
||||
.map(|timestamp| timestamp as i64),
|
||||
})
|
||||
.collect();
|
||||
|
||||
Ok(branches)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update_or_create_branch(
|
||||
&self,
|
||||
repository: ProjectPath,
|
||||
new_branch: String,
|
||||
cx: &AppContext,
|
||||
) -> Task<Result<()>> {
|
||||
let Some(worktree) = self.worktree_for_id(repository.worktree_id, cx) else {
|
||||
return Task::ready(Err(anyhow!("No worktree found for ProjectPath")));
|
||||
};
|
||||
|
||||
match worktree.read(cx) {
|
||||
Worktree::Local(local_worktree) => {
|
||||
let result = util::maybe!({
|
||||
let worktree_error = |error| {
|
||||
format!(
|
||||
"{} for worktree {}",
|
||||
error,
|
||||
local_worktree.abs_path().to_string_lossy()
|
||||
)
|
||||
};
|
||||
|
||||
let entry = local_worktree
|
||||
.git_entry(repository.path)
|
||||
.with_context(|| worktree_error("No git entry found"))?;
|
||||
|
||||
let repo = local_worktree
|
||||
.get_local_repo(&entry)
|
||||
.with_context(|| worktree_error("No repository found"))?
|
||||
.repo()
|
||||
.clone();
|
||||
|
||||
if !repo.branch_exits(&new_branch)? {
|
||||
repo.create_branch(&new_branch)?;
|
||||
}
|
||||
|
||||
repo.change_branch(&new_branch)?;
|
||||
|
||||
Ok(())
|
||||
});
|
||||
|
||||
Task::ready(result)
|
||||
}
|
||||
Worktree::Remote(remote_worktree) => {
|
||||
let request = remote_worktree.client().request(proto::UpdateGitBranch {
|
||||
project_id: remote_worktree.project_id(),
|
||||
repository: Some(proto::ProjectPath {
|
||||
worktree_id: repository.worktree_id.to_proto(),
|
||||
path: repository.path.to_string_lossy().to_string(), // Root path
|
||||
}),
|
||||
branch_name: new_branch,
|
||||
});
|
||||
|
||||
cx.background_executor().spawn(async move {
|
||||
request.await?;
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn filter_paths(
|
||||
fs: &Arc<dyn Fs>,
|
||||
mut input: Receiver<MatchingEntry>,
|
||||
|
@ -917,6 +1051,61 @@ impl WorktreeStore {
|
|||
.ok_or_else(|| anyhow!("invalid request"))?;
|
||||
Worktree::handle_expand_entry(worktree, envelope.payload, cx).await
|
||||
}
|
||||
|
||||
pub async fn handle_git_branches(
|
||||
this: Model<Self>,
|
||||
branches: TypedEnvelope<proto::GitBranches>,
|
||||
cx: AsyncAppContext,
|
||||
) -> Result<proto::GitBranchesResponse> {
|
||||
let project_path = branches
|
||||
.payload
|
||||
.repository
|
||||
.clone()
|
||||
.context("Invalid GitBranches call")?;
|
||||
let project_path = ProjectPath {
|
||||
worktree_id: WorktreeId::from_proto(project_path.worktree_id),
|
||||
path: Path::new(&project_path.path).into(),
|
||||
};
|
||||
|
||||
let branches = this
|
||||
.read_with(&cx, |this, cx| this.branches(project_path, cx))?
|
||||
.await?;
|
||||
|
||||
Ok(proto::GitBranchesResponse {
|
||||
branches: branches
|
||||
.into_iter()
|
||||
.map(|branch| proto::Branch {
|
||||
is_head: branch.is_head,
|
||||
name: branch.name.to_string(),
|
||||
unix_timestamp: branch.unix_timestamp.map(|timestamp| timestamp as u64),
|
||||
})
|
||||
.collect(),
|
||||
})
|
||||
}
|
||||
|
||||
pub async fn handle_update_branch(
|
||||
this: Model<Self>,
|
||||
update_branch: TypedEnvelope<proto::UpdateGitBranch>,
|
||||
cx: AsyncAppContext,
|
||||
) -> Result<proto::Ack> {
|
||||
let project_path = update_branch
|
||||
.payload
|
||||
.repository
|
||||
.clone()
|
||||
.context("Invalid GitBranches call")?;
|
||||
let project_path = ProjectPath {
|
||||
worktree_id: WorktreeId::from_proto(project_path.worktree_id),
|
||||
path: Path::new(&project_path.path).into(),
|
||||
};
|
||||
let new_branch = update_branch.payload.branch_name;
|
||||
|
||||
this.read_with(&cx, |this, cx| {
|
||||
this.update_or_create_branch(project_path, new_branch, cx)
|
||||
})?
|
||||
.await?;
|
||||
|
||||
Ok(proto::Ack {})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue