Fix git branches in non-active repository (#26148)
Release Notes: - Git Beta: Fixed a bug where the branch selector would only show for the first repository opened. --------- Co-authored-by: Conrad Irwin <conrad.irwin@gmail.com> Co-authored-by: Richard Feldman <oss@rtfeldman.com>
This commit is contained in:
parent
2a919ad1d0
commit
9d54e63a11
12 changed files with 316 additions and 299 deletions
|
@ -96,6 +96,9 @@ impl GitStore {
|
|||
|
||||
pub fn init(client: &AnyProtoClient) {
|
||||
client.add_entity_request_handler(Self::handle_get_remotes);
|
||||
client.add_entity_request_handler(Self::handle_get_branches);
|
||||
client.add_entity_request_handler(Self::handle_change_branch);
|
||||
client.add_entity_request_handler(Self::handle_create_branch);
|
||||
client.add_entity_request_handler(Self::handle_push);
|
||||
client.add_entity_request_handler(Self::handle_pull);
|
||||
client.add_entity_request_handler(Self::handle_fetch);
|
||||
|
@ -459,6 +462,67 @@ impl GitStore {
|
|||
})
|
||||
}
|
||||
|
||||
async fn handle_get_branches(
|
||||
this: Entity<Self>,
|
||||
envelope: TypedEnvelope<proto::GitGetBranches>,
|
||||
mut cx: AsyncApp,
|
||||
) -> Result<proto::GitBranchesResponse> {
|
||||
let worktree_id = WorktreeId::from_proto(envelope.payload.worktree_id);
|
||||
let work_directory_id = ProjectEntryId::from_proto(envelope.payload.work_directory_id);
|
||||
let repository_handle =
|
||||
Self::repository_for_request(&this, worktree_id, work_directory_id, &mut cx)?;
|
||||
|
||||
let branches = repository_handle
|
||||
.update(&mut cx, |repository_handle, _| repository_handle.branches())?
|
||||
.await??;
|
||||
|
||||
Ok(proto::GitBranchesResponse {
|
||||
branches: branches
|
||||
.into_iter()
|
||||
.map(|branch| worktree::branch_to_proto(&branch))
|
||||
.collect::<Vec<_>>(),
|
||||
})
|
||||
}
|
||||
async fn handle_create_branch(
|
||||
this: Entity<Self>,
|
||||
envelope: TypedEnvelope<proto::GitCreateBranch>,
|
||||
mut cx: AsyncApp,
|
||||
) -> Result<proto::Ack> {
|
||||
let worktree_id = WorktreeId::from_proto(envelope.payload.worktree_id);
|
||||
let work_directory_id = ProjectEntryId::from_proto(envelope.payload.work_directory_id);
|
||||
let repository_handle =
|
||||
Self::repository_for_request(&this, worktree_id, work_directory_id, &mut cx)?;
|
||||
let branch_name = envelope.payload.branch_name;
|
||||
|
||||
repository_handle
|
||||
.update(&mut cx, |repository_handle, _| {
|
||||
repository_handle.create_branch(branch_name)
|
||||
})?
|
||||
.await??;
|
||||
|
||||
Ok(proto::Ack {})
|
||||
}
|
||||
|
||||
async fn handle_change_branch(
|
||||
this: Entity<Self>,
|
||||
envelope: TypedEnvelope<proto::GitChangeBranch>,
|
||||
mut cx: AsyncApp,
|
||||
) -> Result<proto::Ack> {
|
||||
let worktree_id = WorktreeId::from_proto(envelope.payload.worktree_id);
|
||||
let work_directory_id = ProjectEntryId::from_proto(envelope.payload.work_directory_id);
|
||||
let repository_handle =
|
||||
Self::repository_for_request(&this, worktree_id, work_directory_id, &mut cx)?;
|
||||
let branch_name = envelope.payload.branch_name;
|
||||
|
||||
repository_handle
|
||||
.update(&mut cx, |repository_handle, _| {
|
||||
repository_handle.change_branch(branch_name)
|
||||
})?
|
||||
.await??;
|
||||
|
||||
Ok(proto::Ack {})
|
||||
}
|
||||
|
||||
async fn handle_show(
|
||||
this: Entity<Self>,
|
||||
envelope: TypedEnvelope<proto::GitShow>,
|
||||
|
@ -1279,4 +1343,84 @@ impl Repository {
|
|||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub fn branches(&self) -> oneshot::Receiver<Result<Vec<Branch>>> {
|
||||
self.send_job(|repo| async move {
|
||||
match repo {
|
||||
GitRepo::Local(git_repository) => git_repository.branches(),
|
||||
GitRepo::Remote {
|
||||
project_id,
|
||||
client,
|
||||
worktree_id,
|
||||
work_directory_id,
|
||||
} => {
|
||||
let response = client
|
||||
.request(proto::GitGetBranches {
|
||||
project_id: project_id.0,
|
||||
worktree_id: worktree_id.to_proto(),
|
||||
work_directory_id: work_directory_id.to_proto(),
|
||||
})
|
||||
.await?;
|
||||
|
||||
let branches = response
|
||||
.branches
|
||||
.into_iter()
|
||||
.map(|branch| worktree::proto_to_branch(&branch))
|
||||
.collect();
|
||||
|
||||
Ok(branches)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub fn create_branch(&self, branch_name: String) -> oneshot::Receiver<Result<()>> {
|
||||
self.send_job(|repo| async move {
|
||||
match repo {
|
||||
GitRepo::Local(git_repository) => git_repository.create_branch(&branch_name),
|
||||
GitRepo::Remote {
|
||||
project_id,
|
||||
client,
|
||||
worktree_id,
|
||||
work_directory_id,
|
||||
} => {
|
||||
client
|
||||
.request(proto::GitCreateBranch {
|
||||
project_id: project_id.0,
|
||||
worktree_id: worktree_id.to_proto(),
|
||||
work_directory_id: work_directory_id.to_proto(),
|
||||
branch_name,
|
||||
})
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub fn change_branch(&self, branch_name: String) -> oneshot::Receiver<Result<()>> {
|
||||
self.send_job(|repo| async move {
|
||||
match repo {
|
||||
GitRepo::Local(git_repository) => git_repository.change_branch(&branch_name),
|
||||
GitRepo::Remote {
|
||||
project_id,
|
||||
client,
|
||||
worktree_id,
|
||||
work_directory_id,
|
||||
} => {
|
||||
client
|
||||
.request(proto::GitChangeBranch {
|
||||
project_id: project_id.0,
|
||||
worktree_id: worktree_id.to_proto(),
|
||||
work_directory_id: work_directory_id.to_proto(),
|
||||
branch_name,
|
||||
})
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -48,7 +48,7 @@ use image_store::{ImageItemEvent, ImageStoreEvent};
|
|||
|
||||
use ::git::{
|
||||
blame::Blame,
|
||||
repository::{Branch, GitRepository, RepoPath},
|
||||
repository::{GitRepository, RepoPath},
|
||||
status::FileStatus,
|
||||
};
|
||||
use gpui::{
|
||||
|
@ -3705,21 +3705,6 @@ impl Project {
|
|||
worktree.get_local_repo(&root_entry)?.repo().clone().into()
|
||||
}
|
||||
|
||||
pub fn branches(&self, project_path: ProjectPath, cx: &App) -> Task<Result<Vec<Branch>>> {
|
||||
self.worktree_store().read(cx).branches(project_path, cx)
|
||||
}
|
||||
|
||||
pub fn update_or_create_branch(
|
||||
&self,
|
||||
repository: ProjectPath,
|
||||
new_branch: String,
|
||||
cx: &App,
|
||||
) -> Task<Result<()>> {
|
||||
self.worktree_store()
|
||||
.read(cx)
|
||||
.update_or_create_branch(repository, new_branch, cx)
|
||||
}
|
||||
|
||||
pub fn blame_buffer(
|
||||
&self,
|
||||
buffer: &Entity<Buffer>,
|
||||
|
|
|
@ -27,10 +27,7 @@ use smol::{
|
|||
};
|
||||
use text::ReplicaId;
|
||||
use util::{paths::SanitizedPath, ResultExt};
|
||||
use worktree::{
|
||||
branch_to_proto, Entry, ProjectEntryId, UpdatedEntriesSet, Worktree, WorktreeId,
|
||||
WorktreeSettings,
|
||||
};
|
||||
use worktree::{Entry, ProjectEntryId, UpdatedEntriesSet, Worktree, WorktreeId, WorktreeSettings};
|
||||
|
||||
use crate::{search::SearchQuery, ProjectPath};
|
||||
|
||||
|
@ -83,8 +80,6 @@ impl WorktreeStore {
|
|||
client.add_entity_request_handler(Self::handle_delete_project_entry);
|
||||
client.add_entity_request_handler(Self::handle_expand_project_entry);
|
||||
client.add_entity_request_handler(Self::handle_expand_all_for_project_entry);
|
||||
client.add_entity_request_handler(Self::handle_git_branches);
|
||||
client.add_entity_request_handler(Self::handle_update_branch);
|
||||
}
|
||||
|
||||
pub fn local(retain_worktrees: bool, fs: Arc<dyn Fs>) -> Self {
|
||||
|
@ -890,150 +885,6 @@ impl WorktreeStore {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
pub fn branches(
|
||||
&self,
|
||||
project_path: ProjectPath,
|
||||
cx: &App,
|
||||
) -> 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_proto(), // Root path
|
||||
}),
|
||||
});
|
||||
|
||||
cx.background_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(),
|
||||
upstream: proto_branch.upstream.map(|upstream| {
|
||||
git::repository::Upstream {
|
||||
ref_name: upstream.ref_name.into(),
|
||||
tracking: upstream
|
||||
.tracking
|
||||
.map(|tracking| {
|
||||
git::repository::UpstreamTracking::Tracked(
|
||||
git::repository::UpstreamTrackingStatus {
|
||||
ahead: tracking.ahead as u32,
|
||||
behind: tracking.behind as u32,
|
||||
},
|
||||
)
|
||||
})
|
||||
.unwrap_or(git::repository::UpstreamTracking::Gone),
|
||||
}
|
||||
}),
|
||||
most_recent_commit: proto_branch.most_recent_commit.map(|commit| {
|
||||
git::repository::CommitSummary {
|
||||
sha: commit.sha.into(),
|
||||
subject: commit.subject.into(),
|
||||
commit_timestamp: commit.commit_timestamp,
|
||||
}
|
||||
}),
|
||||
})
|
||||
.collect();
|
||||
|
||||
Ok(branches)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update_or_create_branch(
|
||||
&self,
|
||||
repository: ProjectPath,
|
||||
new_branch: String,
|
||||
cx: &App,
|
||||
) -> 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_proto(), // Root path
|
||||
}),
|
||||
branch_name: new_branch,
|
||||
});
|
||||
|
||||
cx.background_spawn(async move {
|
||||
request.await?;
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn filter_paths(
|
||||
fs: &Arc<dyn Fs>,
|
||||
mut input: Receiver<MatchingEntry>,
|
||||
|
@ -1130,54 +981,6 @@ impl WorktreeStore {
|
|||
.ok_or_else(|| anyhow!("invalid request"))?;
|
||||
Worktree::handle_expand_all_for_entry(worktree, envelope.payload, cx).await
|
||||
}
|
||||
|
||||
pub async fn handle_git_branches(
|
||||
this: Entity<Self>,
|
||||
branches: TypedEnvelope<proto::GitBranches>,
|
||||
cx: AsyncApp,
|
||||
) -> 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: Arc::<Path>::from_proto(project_path.path),
|
||||
};
|
||||
|
||||
let branches = this
|
||||
.read_with(&cx, |this, cx| this.branches(project_path, cx))?
|
||||
.await?;
|
||||
|
||||
Ok(proto::GitBranchesResponse {
|
||||
branches: branches.iter().map(branch_to_proto).collect(),
|
||||
})
|
||||
}
|
||||
|
||||
pub async fn handle_update_branch(
|
||||
this: Entity<Self>,
|
||||
update_branch: TypedEnvelope<proto::UpdateGitBranch>,
|
||||
cx: AsyncApp,
|
||||
) -> 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: Arc::<Path>::from_proto(project_path.path),
|
||||
};
|
||||
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