Add support for remote branches to the branch picker (#28978)

Mikayla Maki and Cole Miller created

Release Notes:

- Added support for remote branches to the branch picker

---------

Co-authored-by: Cole Miller <m@cole-miller.net>

Change summary

crates/git/src/repository.rs | 41 ++++++++++++++++++++++++++++---------
1 file changed, 31 insertions(+), 10 deletions(-)

Detailed changes

crates/git/src/repository.rs 🔗

@@ -772,7 +772,13 @@ impl GitRepository for RealGitRepository {
                 "%(contents:subject)",
             ]
             .join("%00");
-            let args = vec!["for-each-ref", "refs/heads/**/*", "--format", &fields];
+            let args = vec![
+                "for-each-ref",
+                "refs/heads/**/*",
+                "refs/remotes/**/*",
+                "--format",
+                &fields,
+            ];
             let working_directory = working_directory?;
             let output = new_smol_command(&git_binary_path)
                 .current_dir(&working_directory)
@@ -823,8 +829,21 @@ impl GitRepository for RealGitRepository {
         self.executor
             .spawn(async move {
                 let repo = repo.lock();
-                let revision = repo.find_branch(&name, BranchType::Local)?;
-                let revision = revision.get();
+                let branch = if let Ok(branch) = repo.find_branch(&name, BranchType::Local) {
+                    branch
+                } else if let Ok(revision) = repo.find_branch(&name, BranchType::Remote) {
+                    let (_, branch_name) =
+                        name.split_once("/").context("Unexpected branch format")?;
+                    let revision = revision.get();
+                    let branch_commit = revision.peel_to_commit()?;
+                    let mut branch = repo.branch(&branch_name, &branch_commit, false)?;
+                    branch.set_upstream(Some(&name))?;
+                    branch
+                } else {
+                    return Err(anyhow!("Branch not found"));
+                };
+
+                let revision = branch.get();
                 let as_tree = revision.peel_to_tree()?;
                 repo.checkout_tree(as_tree.as_object(), None)?;
                 repo.set_head(
@@ -1637,13 +1656,15 @@ fn parse_branch_input(input: &str) -> Result<Vec<Branch>> {
         let is_current_branch = fields.next().context("no HEAD")? == "*";
         let head_sha: SharedString = fields.next().context("no objectname")?.to_string().into();
         let parent_sha: SharedString = fields.next().context("no parent")?.to_string().into();
-        let ref_name: SharedString = fields
-            .next()
-            .context("no refname")?
-            .strip_prefix("refs/heads/")
-            .context("unexpected format for refname")?
-            .to_string()
-            .into();
+        let raw_ref_name = fields.next().context("no refname")?;
+        let ref_name: SharedString =
+            if let Some(ref_name) = raw_ref_name.strip_prefix("refs/heads/") {
+                ref_name.to_string().into()
+            } else if let Some(ref_name) = raw_ref_name.strip_prefix("refs/remotes/") {
+                ref_name.to_string().into()
+            } else {
+                return Err(anyhow!("unexpected format for refname"));
+            };
         let upstream_name = fields.next().context("no upstream")?.to_string();
         let upstream_tracking = parse_upstream_track(fields.next().context("no upstream:track")?)?;
         let commiterdate = fields.next().context("no committerdate")?.parse::<i64>()?;