git: Check push config before falling back to branch remote (#41700)

Closes #26649

Release Notes:

- Improved git support for remotes handling. Git will now check pushRemote and pushDefault configurations before
falling back to branch remote.

---------

Co-authored-by: Jakub Konka <kubkon@jakubkonka.com>
This commit is contained in:
Mayank Verma
2025-11-28 11:47:21 +00:00
committed by GitHub
co-authored by Jakub Konka
parent d877e562ab
commit 87e3d6e014
5 changed files with 87 additions and 23 deletions
+54 -16
View File
@@ -559,7 +559,11 @@ pub trait GitRepository: Send + Sync {
cx: AsyncApp,
) -> BoxFuture<'_, Result<RemoteCommandOutput>>;
fn get_remotes(&self, branch_name: Option<String>) -> BoxFuture<'_, Result<Vec<Remote>>>;
fn get_push_remote(&self, branch: String) -> BoxFuture<'_, Result<Option<Remote>>>;
fn get_branch_remote(&self, branch: String) -> BoxFuture<'_, Result<Option<Remote>>>;
fn get_all_remotes(&self) -> BoxFuture<'_, Result<Vec<Remote>>>;
/// returns a list of remote branches that contain HEAD
fn check_for_pushed_commit(&self) -> BoxFuture<'_, Result<Vec<SharedString>>>;
@@ -1772,29 +1776,63 @@ impl GitRepository for RealGitRepository {
.boxed()
}
fn get_remotes(&self, branch_name: Option<String>) -> BoxFuture<'_, Result<Vec<Remote>>> {
fn get_push_remote(&self, branch: String) -> BoxFuture<'_, Result<Option<Remote>>> {
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<Option<Remote>>> {
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<Vec<Remote>>> {
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)