remote support

Cole Miller created

Change summary

crates/fs/src/fake_git_repo.rs  | 12 ++++---
crates/git/src/repository.rs    |  6 ++--
crates/git_ui/src/git_ui.rs     |  5 ++
crates/project/src/git_store.rs | 50 ++++++++++++++++++++++++++++------
crates/proto/proto/git.proto    |  4 +-
crates/proto/src/proto.rs       |  3 ++
6 files changed, 60 insertions(+), 20 deletions(-)

Detailed changes

crates/fs/src/fake_git_repo.rs 🔗

@@ -1,5 +1,5 @@
 use crate::{FakeFs, FakeFsEntry, Fs};
-use anyhow::{Context as _, Result};
+use anyhow::{Context as _, Result, bail};
 use collections::{HashMap, HashSet};
 use futures::future::{self, BoxFuture, join_all};
 use git::{
@@ -350,11 +350,13 @@ impl GitRepository for FakeGitRepository {
         })
     }
 
-    fn rename_branch(&self, new_name: String) -> BoxFuture<'_, Result<()>> {
+    fn rename_branch(&self, branch: String, new_name: String) -> BoxFuture<'_, Result<()>> {
         self.with_state_async(true, move |state| {
-            if let Some(current_branch) = &state.current_branch_name {
-                state.branches.insert(new_name.clone());
-                state.branches.remove(current_branch);
+            if !state.branches.remove(&branch) {
+                bail!("no such branch: {branch}");
+            }
+            state.branches.insert(new_name.clone());
+            if state.current_branch_name == Some(branch) {
                 state.current_branch_name = Some(new_name);
             }
             Ok(())

crates/git/src/repository.rs 🔗

@@ -370,7 +370,7 @@ pub trait GitRepository: Send + Sync {
 
     fn change_branch(&self, name: String) -> BoxFuture<'_, Result<()>>;
     fn create_branch(&self, name: String) -> BoxFuture<'_, Result<()>>;
-    fn rename_branch(&self, new_name: String) -> BoxFuture<'_, Result<()>>;
+    fn rename_branch(&self, branch: String, new_name: String) -> BoxFuture<'_, Result<()>>;
 
     fn reset(
         &self,
@@ -1131,7 +1131,7 @@ impl GitRepository for RealGitRepository {
             .boxed()
     }
 
-    fn rename_branch(&self, new_name: String) -> BoxFuture<'_, Result<()>> {
+    fn rename_branch(&self, branch: String, new_name: String) -> BoxFuture<'_, Result<()>> {
         let git_binary_path = self.git_binary_path.clone();
         let working_directory = self.working_directory();
         let executor = self.executor.clone();
@@ -1139,7 +1139,7 @@ impl GitRepository for RealGitRepository {
         self.executor
             .spawn(async move {
                 match GitBinary::new(git_binary_path, working_directory?, executor)
-                    .run_with_output(&["branch", "-m", &new_name])
+                    .run_with_output(&["branch", "-m", &branch, &new_name])
                     .await
                 {
                     Ok(_) => Ok(()),

crates/git_ui/src/git_ui.rs 🔗

@@ -276,9 +276,12 @@ impl RenameBranchModal {
         }
 
         let repo = self.repo.clone();
+        let current_branch = self.current_branch.to_string();
         cx.spawn(async move |_, cx| {
             match repo
-                .update(cx, |repo, _| repo.rename_branch(new_name.clone()))?
+                .update(cx, |repo, _| {
+                    repo.rename_branch(current_branch, new_name.clone())
+                })?
                 .await
             {
                 Ok(Ok(_)) => Ok(()),

crates/project/src/git_store.rs 🔗

@@ -417,6 +417,7 @@ impl GitStore {
         client.add_entity_request_handler(Self::handle_get_default_branch);
         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_rename_branch);
         client.add_entity_request_handler(Self::handle_git_init);
         client.add_entity_request_handler(Self::handle_push);
         client.add_entity_request_handler(Self::handle_pull);
@@ -1948,6 +1949,25 @@ impl GitStore {
         Ok(proto::Ack {})
     }
 
+    async fn handle_rename_branch(
+        this: Entity<Self>,
+        envelope: TypedEnvelope<proto::GitRenameBranch>,
+        mut cx: AsyncApp,
+    ) -> Result<proto::Ack> {
+        let repository_id = RepositoryId::from_proto(envelope.payload.repository_id);
+        let repository_handle = Self::repository_for_request(&this, repository_id, &mut cx)?;
+        let branch = envelope.payload.branch;
+        let new_name = envelope.payload.new_name;
+
+        repository_handle
+            .update(&mut cx, |repository_handle, _| {
+                repository_handle.rename_branch(branch, new_name)
+            })?
+            .await??;
+
+        Ok(proto::Ack {})
+    }
+
     async fn handle_show(
         this: Entity<Self>,
         envelope: TypedEnvelope<proto::GitShow>,
@@ -4199,18 +4219,30 @@ impl Repository {
         )
     }
 
-    pub fn rename_branch(&mut self, new_name: String) -> oneshot::Receiver<Result<()>> {
-        let _id = self.id;
+    pub fn rename_branch(
+        &mut self,
+        branch: String,
+        new_name: String,
+    ) -> oneshot::Receiver<Result<()>> {
+        let id = self.id;
         self.send_job(
-            Some(format!("git branch -m {new_name}").into()),
+            Some(format!("git branch -m {branch} {new_name}").into()),
             move |repo, _cx| async move {
                 match repo {
-                    RepositoryState::Local { backend, .. } => backend.rename_branch(new_name).await,
-                    RepositoryState::Remote { .. } => {
-                        // Remote branch renaming not implemented yet
-                        Err(anyhow::anyhow!(
-                            "Branch renaming is not supported for remote repositories yet"
-                        ))
+                    RepositoryState::Local { backend, .. } => {
+                        backend.rename_branch(branch, new_name).await
+                    }
+                    RepositoryState::Remote { project_id, client } => {
+                        client
+                            .request(proto::GitRenameBranch {
+                                project_id: project_id.0,
+                                repository_id: id.to_proto(),
+                                branch,
+                                new_name,
+                            })
+                            .await?;
+
+                        Ok(())
                     }
                 }
             },

crates/proto/proto/git.proto 🔗

@@ -182,8 +182,8 @@ message GitChangeBranch {
 
 message GitRenameBranch {
     uint64 project_id = 1;
-    reserved 2;
-    uint64 repository_id = 3;
+    uint64 repository_id = 2;
+    string branch = 3;
     string new_name = 4;
 }
 

crates/proto/src/proto.rs 🔗

@@ -301,6 +301,7 @@ messages!(
     (AskPassResponse, Background),
     (GitCreateBranch, Background),
     (GitChangeBranch, Background),
+    (GitRenameBranch, Background),
     (CheckForPushedCommits, Background),
     (CheckForPushedCommitsResponse, Background),
     (GitDiff, Background),
@@ -477,6 +478,7 @@ request_messages!(
     (AskPassRequest, AskPassResponse),
     (GitCreateBranch, Ack),
     (GitChangeBranch, Ack),
+    (GitRenameBranch, Ack),
     (CheckForPushedCommits, CheckForPushedCommitsResponse),
     (GitDiff, GitDiffResponse),
     (GitInit, Ack),
@@ -607,6 +609,7 @@ entity_messages!(
     Pull,
     AskPassRequest,
     GitChangeBranch,
+    GitRenameBranch,
     GitCreateBranch,
     CheckForPushedCommits,
     GitDiff,