diff --git a/crates/fs/src/fake_git_repo.rs b/crates/fs/src/fake_git_repo.rs index febef94d8c..05a7515314 100644 --- a/crates/fs/src/fake_git_repo.rs +++ b/crates/fs/src/fake_git_repo.rs @@ -575,7 +575,15 @@ impl GitRepository for FakeGitRepository { unimplemented!() } - fn get_remotes(&self, _branch: Option) -> BoxFuture<'_, Result>> { + fn get_push_remote(&self, _branch: String) -> BoxFuture<'_, Result>> { + unimplemented!() + } + + fn get_branch_remote(&self, _branch: String) -> BoxFuture<'_, Result>> { + unimplemented!() + } + + fn get_all_remotes(&self) -> BoxFuture<'_, Result>> { unimplemented!() } diff --git a/crates/git/src/repository.rs b/crates/git/src/repository.rs index 03b29eb4a7..e4e13bac9d 100644 --- a/crates/git/src/repository.rs +++ b/crates/git/src/repository.rs @@ -559,7 +559,11 @@ pub trait GitRepository: Send + Sync { cx: AsyncApp, ) -> BoxFuture<'_, Result>; - fn get_remotes(&self, branch_name: Option) -> BoxFuture<'_, Result>>; + fn get_push_remote(&self, branch: String) -> BoxFuture<'_, Result>>; + + fn get_branch_remote(&self, branch: String) -> BoxFuture<'_, Result>>; + + fn get_all_remotes(&self) -> BoxFuture<'_, Result>>; /// returns a list of remote branches that contain HEAD fn check_for_pushed_commit(&self) -> BoxFuture<'_, Result>>; @@ -1772,29 +1776,63 @@ impl GitRepository for RealGitRepository { .boxed() } - fn get_remotes(&self, branch_name: Option) -> BoxFuture<'_, Result>> { + fn get_push_remote(&self, branch: String) -> BoxFuture<'_, Result>> { let working_directory = self.working_directory(); let git_binary_path = self.any_git_binary_path.clone(); self.executor .spawn(async move { let working_directory = working_directory?; - if let Some(branch_name) = branch_name { - let output = new_smol_command(&git_binary_path) - .current_dir(&working_directory) - .args(["config", "--get"]) - .arg(format!("branch.{}.remote", branch_name)) - .output() - .await?; + let output = new_smol_command(&git_binary_path) + .current_dir(&working_directory) + .args(["rev-parse", "--abbrev-ref"]) + .arg(format!("{branch}@{{push}}")) + .output() + .await?; + if !output.status.success() { + return Ok(None); + } + let remote_name = String::from_utf8_lossy(&output.stdout) + .split('/') + .next() + .map(|name| Remote { + name: name.trim().to_string().into(), + }); - if output.status.success() { - let remote_name = String::from_utf8_lossy(&output.stdout); + Ok(remote_name) + }) + .boxed() + } - return Ok(vec![Remote { - name: remote_name.trim().to_string().into(), - }]); - } + fn get_branch_remote(&self, branch: String) -> BoxFuture<'_, Result>> { + let working_directory = self.working_directory(); + let git_binary_path = self.any_git_binary_path.clone(); + self.executor + .spawn(async move { + let working_directory = working_directory?; + let output = new_smol_command(&git_binary_path) + .current_dir(&working_directory) + .args(["config", "--get"]) + .arg(format!("branch.{branch}.remote")) + .output() + .await?; + if !output.status.success() { + return Ok(None); } + let remote_name = String::from_utf8_lossy(&output.stdout); + return Ok(Some(Remote { + name: remote_name.trim().to_string().into(), + })); + }) + .boxed() + } + + fn get_all_remotes(&self) -> BoxFuture<'_, Result>> { + let working_directory = self.working_directory(); + let git_binary_path = self.any_git_binary_path.clone(); + self.executor + .spawn(async move { + let working_directory = working_directory?; let output = new_smol_command(&git_binary_path) .current_dir(&working_directory) .args(["remote"]) @@ -1803,7 +1841,7 @@ impl GitRepository for RealGitRepository { anyhow::ensure!( output.status.success(), - "Failed to get remotes:\n{}", + "Failed to get all remotes:\n{}", String::from_utf8_lossy(&output.stderr) ); let remote_names = String::from_utf8_lossy(&output.stdout) diff --git a/crates/git_ui/src/git_panel.rs b/crates/git_ui/src/git_panel.rs index cf6babb401..a35fda8f6b 100644 --- a/crates/git_ui/src/git_panel.rs +++ b/crates/git_ui/src/git_panel.rs @@ -2111,7 +2111,7 @@ impl GitPanel { cx.spawn_in(window, async move |_, cx| { let repo = repo?; let remotes = repo - .update(cx, |repo, _| repo.get_remotes(None)) + .update(cx, |repo, _| repo.get_remotes(None, false)) .ok()? .await .ok()? @@ -2375,7 +2375,7 @@ impl GitPanel { }; telemetry::event!("Git Pulled"); let branch = branch.clone(); - let remote = self.get_remote(false, window, cx); + let remote = self.get_remote(false, false, window, cx); cx.spawn_in(window, async move |this, cx| { let remote = match remote.await { Ok(Some(remote)) => remote, @@ -2451,7 +2451,7 @@ impl GitPanel { _ => None, } }; - let remote = self.get_remote(select_remote, window, cx); + let remote = self.get_remote(select_remote, true, window, cx); cx.spawn_in(window, async move |this, cx| { let remote = match remote.await { @@ -2528,6 +2528,7 @@ impl GitPanel { fn get_remote( &mut self, always_select: bool, + is_push: bool, window: &mut Window, cx: &mut Context, ) -> impl Future>> + use<> { @@ -2545,7 +2546,7 @@ impl GitPanel { let current_branch = repo.branch.as_ref().context("No active branch")?; Some(current_branch.name().to_string()) }; - anyhow::Ok(repo.get_remotes(current_branch)) + anyhow::Ok(repo.get_remotes(current_branch, is_push)) })?? .await??; diff --git a/crates/project/src/git_store.rs b/crates/project/src/git_store.rs index d0888d9693..055b91f2ee 100644 --- a/crates/project/src/git_store.rs +++ b/crates/project/src/git_store.rs @@ -2096,10 +2096,11 @@ impl GitStore { let repository_handle = Self::repository_for_request(&this, repository_id, &mut cx)?; let branch_name = envelope.payload.branch_name; + let is_push = envelope.payload.is_push; let remotes = repository_handle .update(&mut cx, |repository_handle, _| { - repository_handle.get_remotes(branch_name) + repository_handle.get_remotes(branch_name, is_push) })? .await??; @@ -4737,12 +4738,26 @@ impl Repository { pub fn get_remotes( &mut self, branch_name: Option, + is_push: bool, ) -> oneshot::Receiver>> { let id = self.id; self.send_job(None, move |repo, _cx| async move { match repo { RepositoryState::Local(LocalRepositoryState { backend, .. }) => { - backend.get_remotes(branch_name).await + let remote = if let Some(branch_name) = branch_name { + if is_push { + backend.get_push_remote(branch_name).await? + } else { + backend.get_branch_remote(branch_name).await? + } + } else { + None + }; + + match remote { + Some(remote) => Ok(vec![remote]), + None => backend.get_all_remotes().await, + } } RepositoryState::Remote(RemoteRepositoryState { project_id, client }) => { let response = client @@ -4750,6 +4765,7 @@ impl Repository { project_id: project_id.0, repository_id: id.to_proto(), branch_name, + is_push, }) .await?; diff --git a/crates/proto/proto/git.proto b/crates/proto/proto/git.proto index 07fab2065b..3dcd71fe44 100644 --- a/crates/proto/proto/git.proto +++ b/crates/proto/proto/git.proto @@ -389,6 +389,7 @@ message GetRemotes { reserved 2; uint64 repository_id = 3; optional string branch_name = 4; + bool is_push = 5; } message GetRemotesResponse {