git: Add `PushTo` to select which remote to push (#31482)

CharlesChen0823 and Cole Miller created

mostly, I using `git checkout -b branch_name upstream/main` to create
new branch which reference remote upstream not my fork.
When using `Push` will always failed with not permission. So we need
ability to select which remote to push.

Current branch is based on my previous pr #26897 

Release Notes:

- Add `PushTo` to select which remote to push.

---------

Co-authored-by: Cole Miller <cole@zed.dev>

Change summary

crates/git/src/git.rs          |  1 +
crates/git_ui/src/git_panel.rs | 24 ++++++++++++++++++------
crates/git_ui/src/git_ui.rs    | 13 +++++++++++--
3 files changed, 30 insertions(+), 8 deletions(-)

Detailed changes

crates/git/src/git.rs 🔗

@@ -46,6 +46,7 @@ actions!(
         TrashUntrackedFiles,
         Uncommit,
         Push,
+        PushTo,
         ForcePush,
         Pull,
         Fetch,

crates/git_ui/src/git_panel.rs 🔗

@@ -2025,7 +2025,7 @@ impl GitPanel {
         };
         telemetry::event!("Git Pulled");
         let branch = branch.clone();
-        let remote = self.get_current_remote(window, cx);
+        let remote = self.get_remote(false, window, cx);
         cx.spawn_in(window, async move |this, cx| {
             let remote = match remote.await {
                 Ok(Some(remote)) => remote,
@@ -2070,7 +2070,13 @@ impl GitPanel {
         .detach_and_log_err(cx);
     }
 
-    pub(crate) fn push(&mut self, force_push: bool, window: &mut Window, cx: &mut Context<Self>) {
+    pub(crate) fn push(
+        &mut self,
+        force_push: bool,
+        select_remote: bool,
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) {
         if !self.can_push_and_pull(cx) {
             return;
         }
@@ -2095,7 +2101,7 @@ impl GitPanel {
                 _ => None,
             }
         };
-        let remote = self.get_current_remote(window, cx);
+        let remote = self.get_remote(select_remote, window, cx);
 
         cx.spawn_in(window, async move |this, cx| {
             let remote = match remote.await {
@@ -2169,8 +2175,9 @@ impl GitPanel {
         !self.project.read(cx).is_via_collab()
     }
 
-    fn get_current_remote(
+    fn get_remote(
         &mut self,
+        always_select: bool,
         window: &mut Window,
         cx: &mut Context<Self>,
     ) -> impl Future<Output = anyhow::Result<Option<Remote>>> + use<> {
@@ -2182,8 +2189,13 @@ impl GitPanel {
             let repo = repo.context("No active repository")?;
             let current_remotes: Vec<Remote> = repo
                 .update(&mut cx, |repo, _| {
-                    let current_branch = repo.branch.as_ref().context("No active branch")?;
-                    anyhow::Ok(repo.get_remotes(Some(current_branch.name().to_string())))
+                    let current_branch = if always_select {
+                        None
+                    } else {
+                        let current_branch = repo.branch.as_ref().context("No active branch")?;
+                        Some(current_branch.name().to_string())
+                    };
+                    anyhow::Ok(repo.get_remotes(current_branch))
                 })??
                 .await??;
 

crates/git_ui/src/git_ui.rs 🔗

@@ -75,7 +75,15 @@ pub fn init(cx: &mut App) {
                     return;
                 };
                 panel.update(cx, |panel, cx| {
-                    panel.push(false, window, cx);
+                    panel.push(false, false, window, cx);
+                });
+            });
+            workspace.register_action(|workspace, _: &git::PushTo, window, cx| {
+                let Some(panel) = workspace.panel::<git_panel::GitPanel>(cx) else {
+                    return;
+                };
+                panel.update(cx, |panel, cx| {
+                    panel.push(false, true, window, cx);
                 });
             });
             workspace.register_action(|workspace, _: &git::ForcePush, window, cx| {
@@ -83,7 +91,7 @@ pub fn init(cx: &mut App) {
                     return;
                 };
                 panel.update(cx, |panel, cx| {
-                    panel.push(true, window, cx);
+                    panel.push(true, false, window, cx);
                 });
             });
             workspace.register_action(|workspace, _: &git::Pull, window, cx| {
@@ -379,6 +387,7 @@ mod remote_button {
                         .action("Pull", git::Pull.boxed_clone())
                         .separator()
                         .action("Push", git::Push.boxed_clone())
+                        .action("Push To", git::PushTo.boxed_clone())
                         .action("Force Push", git::ForcePush.boxed_clone())
                 }))
             })