Simplify project git code (#25662)

This was originally a part of another PR, but I wanted to get the
refactoring in and shift focus to working on bugs.

This causes all git commands via the `Repository` entity to be
serialized, and allows us to return values other than `Result<()>`

Release Notes:

- N/A
This commit is contained in:
Mikayla Maki
2025-02-26 18:16:10 +00:00
committed by GitHub
parent 78da39e19b
commit 5edded5c02
3 changed files with 365 additions and 433 deletions
+6 -9
View File
@@ -618,10 +618,9 @@ impl GitRepository for RealGitRepository {
"Failed to push:\n{}", "Failed to push:\n{}",
String::from_utf8_lossy(&output.stderr) String::from_utf8_lossy(&output.stderr)
)); ));
} else {
Ok(())
} }
// TODO: Get remote response out of this and show it to the user
Ok(())
} }
fn pull(&self, branch_name: &str, remote_name: &str) -> Result<()> { fn pull(&self, branch_name: &str, remote_name: &str) -> Result<()> {
@@ -639,10 +638,9 @@ impl GitRepository for RealGitRepository {
"Failed to pull:\n{}", "Failed to pull:\n{}",
String::from_utf8_lossy(&output.stderr) String::from_utf8_lossy(&output.stderr)
)); ));
} else {
return Ok(());
} }
// TODO: Get remote response out of this and show it to the user
Ok(())
} }
fn fetch(&self) -> Result<()> { fn fetch(&self) -> Result<()> {
@@ -658,10 +656,9 @@ impl GitRepository for RealGitRepository {
"Failed to fetch:\n{}", "Failed to fetch:\n{}",
String::from_utf8_lossy(&output.stderr) String::from_utf8_lossy(&output.stderr)
)); ));
} else {
return Ok(());
} }
// TODO: Get remote response out of this and show it to the user
Ok(())
} }
fn get_remotes(&self, branch_name: Option<&str>) -> Result<Vec<Remote>> { fn get_remotes(&self, branch_name: Option<&str>) -> Result<Vec<Remote>> {
+6 -4
View File
@@ -1386,14 +1386,14 @@ impl GitPanel {
}; };
let mut current_remotes: Vec<Remote> = repo let mut current_remotes: Vec<Remote> = repo
.update(&mut cx, |repo, cx| { .update(&mut cx, |repo, _| {
let Some(current_branch) = repo.current_branch() else { let Some(current_branch) = repo.current_branch() else {
return Err(anyhow::anyhow!("No active branch")); return Err(anyhow::anyhow!("No active branch"));
}; };
Ok(repo.get_remotes(Some(current_branch.name.to_string()), cx)) Ok(repo.get_remotes(Some(current_branch.name.to_string())))
})?? })??
.await?; .await??;
if current_remotes.len() == 0 { if current_remotes.len() == 0 {
return Err(anyhow::anyhow!("No active remote")); return Err(anyhow::anyhow!("No active remote"));
@@ -2357,7 +2357,9 @@ impl GitPanel {
let Some(repo) = self.active_repository.clone() else { let Some(repo) = self.active_repository.clone() else {
return Task::ready(Err(anyhow::anyhow!("no active repo"))); return Task::ready(Err(anyhow::anyhow!("no active repo")));
}; };
repo.update(cx, |repo, cx| repo.show(sha, cx))
let show = repo.read(cx).show(sha);
cx.spawn(|_, _| async move { show.await? })
} }
fn deploy_entry_context_menu( fn deploy_entry_context_menu(
+353 -420
View File
@@ -11,26 +11,29 @@ use git::{
status::{GitSummary, TrackedSummary}, status::{GitSummary, TrackedSummary},
}; };
use gpui::{ use gpui::{
App, AppContext as _, AsyncApp, Context, Entity, EventEmitter, SharedString, Subscription, App, AppContext, AsyncApp, Context, Entity, EventEmitter, SharedString, Subscription, Task,
Task, WeakEntity, WeakEntity,
}; };
use language::{Buffer, LanguageRegistry}; use language::{Buffer, LanguageRegistry};
use rpc::proto::{git_reset, ToProto}; use rpc::proto::{git_reset, ToProto};
use rpc::{proto, AnyProtoClient, TypedEnvelope}; use rpc::{proto, AnyProtoClient, TypedEnvelope};
use settings::WorktreeId; use settings::WorktreeId;
use std::future::Future;
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use std::sync::Arc; use std::sync::Arc;
use text::BufferId; use text::BufferId;
use util::{maybe, ResultExt}; use util::{maybe, ResultExt};
use worktree::{ProjectEntryId, RepositoryEntry, StatusEntry}; use worktree::{ProjectEntryId, RepositoryEntry, StatusEntry};
type GitJob = Box<dyn FnOnce(&mut AsyncApp) -> Task<()>>;
pub struct GitStore { pub struct GitStore {
buffer_store: Entity<BufferStore>, buffer_store: Entity<BufferStore>,
pub(super) project_id: Option<ProjectId>, pub(super) project_id: Option<ProjectId>,
pub(super) client: Option<AnyProtoClient>, pub(super) client: Option<AnyProtoClient>,
repositories: Vec<Entity<Repository>>, repositories: Vec<Entity<Repository>>,
active_index: Option<usize>, active_index: Option<usize>,
update_sender: mpsc::UnboundedSender<(Message, oneshot::Sender<Result<()>>)>, update_sender: mpsc::UnboundedSender<GitJob>,
_subscription: Subscription, _subscription: Subscription,
} }
@@ -41,7 +44,7 @@ pub struct Repository {
pub repository_entry: RepositoryEntry, pub repository_entry: RepositoryEntry,
pub git_repo: GitRepo, pub git_repo: GitRepo,
pub merge_message: Option<String>, pub merge_message: Option<String>,
update_sender: mpsc::UnboundedSender<(Message, oneshot::Sender<Result<()>>)>, job_sender: mpsc::UnboundedSender<GitJob>,
} }
#[derive(Clone)] #[derive(Clone)]
@@ -55,40 +58,6 @@ pub enum GitRepo {
}, },
} }
pub enum Message {
Commit {
git_repo: GitRepo,
message: SharedString,
name_and_email: Option<(SharedString, SharedString)>,
},
Reset {
repo: GitRepo,
commit: SharedString,
reset_mode: ResetMode,
},
CheckoutFiles {
repo: GitRepo,
commit: SharedString,
paths: Vec<RepoPath>,
},
Stage(GitRepo, Vec<RepoPath>),
Unstage(GitRepo, Vec<RepoPath>),
SetIndexText(GitRepo, RepoPath, Option<String>),
Push {
repo: GitRepo,
branch_name: SharedString,
remote_name: SharedString,
options: Option<PushOptions>,
},
Pull {
repo: GitRepo,
branch_name: SharedString,
remote_name: SharedString,
},
Fetch(GitRepo),
}
#[derive(Debug)]
pub enum GitEvent { pub enum GitEvent {
ActiveRepositoryChanged, ActiveRepositoryChanged,
FileSystemUpdated, FileSystemUpdated,
@@ -220,7 +189,7 @@ impl GitStore {
worktree_id, worktree_id,
repository_entry: repo.clone(), repository_entry: repo.clone(),
git_repo, git_repo,
update_sender: self.update_sender.clone(), job_sender: self.update_sender.clone(),
merge_message, merge_message,
commit_message_buffer: None, commit_message_buffer: None,
}) })
@@ -252,272 +221,15 @@ impl GitStore {
self.repositories.clone() self.repositories.clone()
} }
fn spawn_git_worker( fn spawn_git_worker(cx: &mut Context<'_, GitStore>) -> mpsc::UnboundedSender<GitJob> {
cx: &mut Context<'_, GitStore>, let (job_tx, mut job_rx) = mpsc::unbounded::<GitJob>();
) -> mpsc::UnboundedSender<(Message, oneshot::Sender<Result<()>>)> { cx.spawn(|_, mut cx| async move {
let (update_sender, mut update_receiver) = while let Some(job) = job_rx.next().await {
mpsc::unbounded::<(Message, oneshot::Sender<Result<()>>)>(); job(&mut cx).await
cx.spawn(|_, cx| async move {
while let Some((msg, respond)) = update_receiver.next().await {
if !respond.is_canceled() {
let result = cx.background_spawn(Self::process_git_msg(msg)).await;
respond.send(result).ok();
}
} }
}) })
.detach(); .detach();
update_sender job_tx
}
async fn process_git_msg(msg: Message) -> Result<()> {
match msg {
Message::Fetch(repo) => {
match repo {
GitRepo::Local(git_repository) => git_repository.fetch()?,
GitRepo::Remote {
project_id,
client,
worktree_id,
work_directory_id,
} => {
client
.request(proto::Fetch {
project_id: project_id.0,
worktree_id: worktree_id.to_proto(),
work_directory_id: work_directory_id.to_proto(),
})
.await
.context("sending fetch request")?;
}
}
Ok(())
}
Message::Pull {
repo,
branch_name,
remote_name,
} => {
match repo {
GitRepo::Local(git_repository) => {
git_repository.pull(&branch_name, &remote_name)?
}
GitRepo::Remote {
project_id,
client,
worktree_id,
work_directory_id,
} => {
client
.request(proto::Pull {
project_id: project_id.0,
worktree_id: worktree_id.to_proto(),
work_directory_id: work_directory_id.to_proto(),
branch_name: branch_name.to_string(),
remote_name: remote_name.to_string(),
})
.await
.context("sending pull request")?;
}
}
Ok(())
}
Message::Push {
repo,
branch_name,
remote_name,
options,
} => {
match repo {
GitRepo::Local(git_repository) => {
git_repository.push(&branch_name, &remote_name, options)?
}
GitRepo::Remote {
project_id,
client,
worktree_id,
work_directory_id,
} => {
client
.request(proto::Push {
project_id: project_id.0,
worktree_id: worktree_id.to_proto(),
work_directory_id: work_directory_id.to_proto(),
branch_name: branch_name.to_string(),
remote_name: remote_name.to_string(),
options: options.map(|options| match options {
PushOptions::Force => proto::push::PushOptions::Force,
PushOptions::SetUpstream => {
proto::push::PushOptions::SetUpstream
}
}
as i32),
})
.await
.context("sending push request")?;
}
}
Ok(())
}
Message::Stage(repo, paths) => {
match repo {
GitRepo::Local(repo) => repo.stage_paths(&paths)?,
GitRepo::Remote {
project_id,
client,
worktree_id,
work_directory_id,
} => {
client
.request(proto::Stage {
project_id: project_id.0,
worktree_id: worktree_id.to_proto(),
work_directory_id: work_directory_id.to_proto(),
paths: paths
.into_iter()
.map(|repo_path| repo_path.as_ref().to_proto())
.collect(),
})
.await
.context("sending stage request")?;
}
}
Ok(())
}
Message::Reset {
repo,
commit,
reset_mode,
} => {
match repo {
GitRepo::Local(repo) => repo.reset(&commit, reset_mode)?,
GitRepo::Remote {
project_id,
client,
worktree_id,
work_directory_id,
} => {
client
.request(proto::GitReset {
project_id: project_id.0,
worktree_id: worktree_id.to_proto(),
work_directory_id: work_directory_id.to_proto(),
commit: commit.into(),
mode: match reset_mode {
ResetMode::Soft => git_reset::ResetMode::Soft.into(),
ResetMode::Mixed => git_reset::ResetMode::Mixed.into(),
},
})
.await?;
}
}
Ok(())
}
Message::CheckoutFiles {
repo,
commit,
paths,
} => {
match repo {
GitRepo::Local(repo) => repo.checkout_files(&commit, &paths)?,
GitRepo::Remote {
project_id,
client,
worktree_id,
work_directory_id,
} => {
client
.request(proto::GitCheckoutFiles {
project_id: project_id.0,
worktree_id: worktree_id.to_proto(),
work_directory_id: work_directory_id.to_proto(),
commit: commit.into(),
paths: paths
.into_iter()
.map(|p| p.to_string_lossy().to_string())
.collect(),
})
.await?;
}
}
Ok(())
}
Message::Unstage(repo, paths) => {
match repo {
GitRepo::Local(repo) => repo.unstage_paths(&paths)?,
GitRepo::Remote {
project_id,
client,
worktree_id,
work_directory_id,
} => {
client
.request(proto::Unstage {
project_id: project_id.0,
worktree_id: worktree_id.to_proto(),
work_directory_id: work_directory_id.to_proto(),
paths: paths
.into_iter()
.map(|repo_path| repo_path.as_ref().to_proto())
.collect(),
})
.await
.context("sending unstage request")?;
}
}
Ok(())
}
Message::Commit {
git_repo,
message,
name_and_email,
} => {
match git_repo {
GitRepo::Local(repo) => repo.commit(
message.as_ref(),
name_and_email
.as_ref()
.map(|(name, email)| (name.as_ref(), email.as_ref())),
)?,
GitRepo::Remote {
project_id,
client,
worktree_id,
work_directory_id,
} => {
let (name, email) = name_and_email.unzip();
client
.request(proto::Commit {
project_id: project_id.0,
worktree_id: worktree_id.to_proto(),
work_directory_id: work_directory_id.to_proto(),
message: String::from(message),
name: name.map(String::from),
email: email.map(String::from),
})
.await
.context("sending commit request")?;
}
}
Ok(())
}
Message::SetIndexText(git_repo, path, text) => match git_repo {
GitRepo::Local(repo) => repo.set_index_text(&path, text),
GitRepo::Remote {
project_id,
client,
worktree_id,
work_directory_id,
} => client.send(proto::SetIndexText {
project_id: project_id.0,
worktree_id: worktree_id.to_proto(),
work_directory_id: work_directory_id.to_proto(),
path: path.as_ref().to_proto(),
text,
}),
},
}
} }
async fn handle_fetch( async fn handle_fetch(
@@ -696,10 +408,10 @@ impl GitStore {
let branch_name = envelope.payload.branch_name; let branch_name = envelope.payload.branch_name;
let remotes = repository_handle let remotes = repository_handle
.update(&mut cx, |repository_handle, cx| { .update(&mut cx, |repository_handle, _| {
repository_handle.get_remotes(branch_name, cx) repository_handle.get_remotes(branch_name)
})? })?
.await?; .await??;
Ok(proto::GetRemotesResponse { Ok(proto::GetRemotesResponse {
remotes: remotes remotes: remotes
@@ -722,10 +434,10 @@ impl GitStore {
Self::repository_for_request(&this, worktree_id, work_directory_id, &mut cx)?; Self::repository_for_request(&this, worktree_id, work_directory_id, &mut cx)?;
let commit = repository_handle let commit = repository_handle
.update(&mut cx, |repository_handle, cx| { .update(&mut cx, |repository_handle, _| {
repository_handle.show(&envelope.payload.commit, cx) repository_handle.show(&envelope.payload.commit)
})? })?
.await?; .await??;
Ok(proto::GitCommitDetails { Ok(proto::GitCommitDetails {
sha: commit.sha.into(), sha: commit.sha.into(),
message: commit.message.into(), message: commit.message.into(),
@@ -854,6 +566,26 @@ impl Repository {
self.repository_entry.branch() self.repository_entry.branch()
} }
fn send_job<F, Fut, R>(&self, job: F) -> oneshot::Receiver<R>
where
F: FnOnce(GitRepo) -> Fut + 'static,
Fut: Future<Output = R> + Send + 'static,
R: Send + 'static,
{
let (result_tx, result_rx) = futures::channel::oneshot::channel();
let git_repo = self.git_repo.clone();
self.job_sender
.unbounded_send(Box::new(|cx: &mut AsyncApp| {
let job = job(git_repo);
cx.background_spawn(async move {
let result = job.await;
result_tx.send(result).ok();
})
}))
.ok();
result_rx
}
pub fn display_name(&self, project: &Project, cx: &App) -> SharedString { pub fn display_name(&self, project: &Project, cx: &App) -> SharedString {
maybe!({ maybe!({
let project_path = self.repo_path_to_project_path(&"".into())?; let project_path = self.repo_path_to_project_path(&"".into())?;
@@ -1004,60 +736,106 @@ impl Repository {
commit: &str, commit: &str,
paths: Vec<RepoPath>, paths: Vec<RepoPath>,
) -> oneshot::Receiver<Result<()>> { ) -> oneshot::Receiver<Result<()>> {
self.send_message(Message::CheckoutFiles { let commit = commit.to_string();
repo: self.git_repo.clone(), self.send_job(|git_repo| async move {
commit: commit.to_string().into(), match git_repo {
paths, GitRepo::Local(repo) => repo.checkout_files(&commit, &paths),
GitRepo::Remote {
project_id,
client,
worktree_id,
work_directory_id,
} => {
client
.request(proto::GitCheckoutFiles {
project_id: project_id.0,
worktree_id: worktree_id.to_proto(),
work_directory_id: work_directory_id.to_proto(),
commit,
paths: paths
.into_iter()
.map(|p| p.to_string_lossy().to_string())
.collect(),
})
.await?;
Ok(())
}
}
}) })
} }
pub fn reset(&self, commit: &str, reset_mode: ResetMode) -> oneshot::Receiver<Result<()>> { pub fn reset(&self, commit: &str, reset_mode: ResetMode) -> oneshot::Receiver<Result<()>> {
self.send_message(Message::Reset { let commit = commit.to_string();
repo: self.git_repo.clone(), self.send_job(|git_repo| async move {
commit: commit.to_string().into(), match git_repo {
reset_mode, GitRepo::Local(git_repo) => git_repo.reset(&commit, reset_mode),
GitRepo::Remote {
project_id,
client,
worktree_id,
work_directory_id,
} => {
client
.request(proto::GitReset {
project_id: project_id.0,
worktree_id: worktree_id.to_proto(),
work_directory_id: work_directory_id.to_proto(),
commit,
mode: match reset_mode {
ResetMode::Soft => git_reset::ResetMode::Soft.into(),
ResetMode::Mixed => git_reset::ResetMode::Mixed.into(),
},
})
.await?;
Ok(())
}
}
}) })
} }
pub fn show(&self, commit: &str, cx: &Context<Self>) -> Task<Result<CommitDetails>> { pub fn show(&self, commit: &str) -> oneshot::Receiver<Result<CommitDetails>> {
let commit = commit.to_string(); let commit = commit.to_string();
match self.git_repo.clone() { self.send_job(|git_repo| async move {
GitRepo::Local(git_repository) => { match git_repo {
let commit = commit.to_string(); GitRepo::Local(git_repository) => git_repository.show(&commit),
cx.background_spawn(async move { git_repository.show(&commit) }) GitRepo::Remote {
} project_id,
GitRepo::Remote { client,
project_id, worktree_id,
client, work_directory_id,
worktree_id, } => {
work_directory_id, let resp = client
} => cx.background_spawn(async move { .request(proto::GitShow {
let resp = client project_id: project_id.0,
.request(proto::GitShow { worktree_id: worktree_id.to_proto(),
project_id: project_id.0, work_directory_id: work_directory_id.to_proto(),
worktree_id: worktree_id.to_proto(), commit,
work_directory_id: work_directory_id.to_proto(), })
commit, .await?;
})
.await?;
Ok(CommitDetails { Ok(CommitDetails {
sha: resp.sha.into(), sha: resp.sha.into(),
message: resp.message.into(), message: resp.message.into(),
commit_timestamp: resp.commit_timestamp, commit_timestamp: resp.commit_timestamp,
committer_email: resp.committer_email.into(), committer_email: resp.committer_email.into(),
committer_name: resp.committer_name.into(), committer_name: resp.committer_name.into(),
}) })
}), }
} }
})
} }
fn buffer_store(&self, cx: &App) -> Option<Entity<BufferStore>> { fn buffer_store(&self, cx: &App) -> Option<Entity<BufferStore>> {
Some(self.git_store.upgrade()?.read(cx).buffer_store.clone()) Some(self.git_store.upgrade()?.read(cx).buffer_store.clone())
} }
pub fn stage_entries(&self, entries: Vec<RepoPath>, cx: &mut App) -> Task<anyhow::Result<()>> { pub fn stage_entries(
let (result_tx, result_rx) = futures::channel::oneshot::channel(); &self,
entries: Vec<RepoPath>,
cx: &mut Context<Self>,
) -> Task<anyhow::Result<()>> {
if entries.is_empty() { if entries.is_empty() {
return Task::ready(Ok(())); return Task::ready(Ok(()));
} }
@@ -1083,16 +861,41 @@ impl Repository {
}) })
} }
let update_sender = self.update_sender.clone(); cx.spawn(|this, mut cx| async move {
let git_repo = self.git_repo.clone();
cx.spawn(|_| async move {
for save_future in save_futures { for save_future in save_futures {
save_future.await?; save_future.await?;
} }
update_sender
.unbounded_send((Message::Stage(git_repo, entries), result_tx)) this.update(&mut cx, |this, _| {
.ok(); this.send_job(|git_repo| async move {
result_rx.await.anyhow()??; match git_repo {
GitRepo::Local(repo) => repo.stage_paths(&entries),
GitRepo::Remote {
project_id,
client,
worktree_id,
work_directory_id,
} => {
client
.request(proto::Stage {
project_id: project_id.0,
worktree_id: worktree_id.to_proto(),
work_directory_id: work_directory_id.to_proto(),
paths: entries
.into_iter()
.map(|repo_path| repo_path.as_ref().to_proto())
.collect(),
})
.await
.context("sending stage request")?;
Ok(())
}
}
})
})?
.await??;
Ok(()) Ok(())
}) })
} }
@@ -1100,9 +903,8 @@ impl Repository {
pub fn unstage_entries( pub fn unstage_entries(
&self, &self,
entries: Vec<RepoPath>, entries: Vec<RepoPath>,
cx: &mut App, cx: &mut Context<Self>,
) -> Task<anyhow::Result<()>> { ) -> Task<anyhow::Result<()>> {
let (result_tx, result_rx) = futures::channel::oneshot::channel();
if entries.is_empty() { if entries.is_empty() {
return Task::ready(Ok(())); return Task::ready(Ok(()));
} }
@@ -1128,21 +930,46 @@ impl Repository {
}) })
} }
let update_sender = self.update_sender.clone(); cx.spawn(move |this, mut cx| async move {
let git_repo = self.git_repo.clone();
cx.spawn(|_| async move {
for save_future in save_futures { for save_future in save_futures {
save_future.await?; save_future.await?;
} }
update_sender
.unbounded_send((Message::Unstage(git_repo, entries), result_tx)) this.update(&mut cx, |this, _| {
.ok(); this.send_job(|git_repo| async move {
result_rx.await.anyhow()??; match git_repo {
GitRepo::Local(repo) => repo.unstage_paths(&entries),
GitRepo::Remote {
project_id,
client,
worktree_id,
work_directory_id,
} => {
client
.request(proto::Unstage {
project_id: project_id.0,
worktree_id: worktree_id.to_proto(),
work_directory_id: work_directory_id.to_proto(),
paths: entries
.into_iter()
.map(|repo_path| repo_path.as_ref().to_proto())
.collect(),
})
.await
.context("sending unstage request")?;
Ok(())
}
}
})
})?
.await??;
Ok(()) Ok(())
}) })
} }
pub fn stage_all(&self, cx: &mut App) -> Task<anyhow::Result<()>> { pub fn stage_all(&self, cx: &mut Context<Self>) -> Task<anyhow::Result<()>> {
let to_stage = self let to_stage = self
.repository_entry .repository_entry
.status() .status()
@@ -1152,7 +979,7 @@ impl Repository {
self.stage_entries(to_stage, cx) self.stage_entries(to_stage, cx)
} }
pub fn unstage_all(&self, cx: &mut App) -> Task<anyhow::Result<()>> { pub fn unstage_all(&self, cx: &mut Context<Self>) -> Task<anyhow::Result<()>> {
let to_unstage = self let to_unstage = self
.repository_entry .repository_entry
.status() .status()
@@ -1185,15 +1012,62 @@ impl Repository {
message: SharedString, message: SharedString,
name_and_email: Option<(SharedString, SharedString)>, name_and_email: Option<(SharedString, SharedString)>,
) -> oneshot::Receiver<Result<()>> { ) -> oneshot::Receiver<Result<()>> {
self.send_message(Message::Commit { self.send_job(|git_repo| async move {
git_repo: self.git_repo.clone(), match git_repo {
message, GitRepo::Local(repo) => repo.commit(
name_and_email, message.as_ref(),
name_and_email
.as_ref()
.map(|(name, email)| (name.as_ref(), email.as_ref())),
),
GitRepo::Remote {
project_id,
client,
worktree_id,
work_directory_id,
} => {
let (name, email) = name_and_email.unzip();
client
.request(proto::Commit {
project_id: project_id.0,
worktree_id: worktree_id.to_proto(),
work_directory_id: work_directory_id.to_proto(),
message: String::from(message),
name: name.map(String::from),
email: email.map(String::from),
})
.await
.context("sending commit request")?;
Ok(())
}
}
}) })
} }
pub fn fetch(&self) -> oneshot::Receiver<Result<()>> { pub fn fetch(&self) -> oneshot::Receiver<Result<()>> {
self.send_message(Message::Fetch(self.git_repo.clone())) self.send_job(|git_repo| async move {
match git_repo {
GitRepo::Local(git_repository) => git_repository.fetch(),
GitRepo::Remote {
project_id,
client,
worktree_id,
work_directory_id,
} => {
client
.request(proto::Fetch {
project_id: project_id.0,
worktree_id: worktree_id.to_proto(),
work_directory_id: work_directory_id.to_proto(),
})
.await
.context("sending fetch request")?;
Ok(())
}
}
})
} }
pub fn push( pub fn push(
@@ -1202,11 +1076,33 @@ impl Repository {
remote: SharedString, remote: SharedString,
options: Option<PushOptions>, options: Option<PushOptions>,
) -> oneshot::Receiver<Result<()>> { ) -> oneshot::Receiver<Result<()>> {
self.send_message(Message::Push { self.send_job(move |git_repo| async move {
repo: self.git_repo.clone(), match git_repo {
branch_name: branch, GitRepo::Local(git_repository) => git_repository.push(&branch, &remote, options),
remote_name: remote, GitRepo::Remote {
options, project_id,
client,
worktree_id,
work_directory_id,
} => {
client
.request(proto::Push {
project_id: project_id.0,
worktree_id: worktree_id.to_proto(),
work_directory_id: work_directory_id.to_proto(),
branch_name: branch.to_string(),
remote_name: remote.to_string(),
options: options.map(|options| match options {
PushOptions::Force => proto::push::PushOptions::Force,
PushOptions::SetUpstream => proto::push::PushOptions::SetUpstream,
} as i32),
})
.await
.context("sending push request")?;
Ok(())
}
}
}) })
} }
@@ -1215,10 +1111,30 @@ impl Repository {
branch: SharedString, branch: SharedString,
remote: SharedString, remote: SharedString,
) -> oneshot::Receiver<Result<()>> { ) -> oneshot::Receiver<Result<()>> {
self.send_message(Message::Pull { self.send_job(|git_repo| async move {
repo: self.git_repo.clone(), match git_repo {
branch_name: branch, GitRepo::Local(git_repository) => git_repository.pull(&branch, &remote),
remote_name: remote, GitRepo::Remote {
project_id,
client,
worktree_id,
work_directory_id,
} => {
client
.request(proto::Pull {
project_id: project_id.0,
worktree_id: worktree_id.to_proto(),
work_directory_id: work_directory_id.to_proto(),
branch_name: branch.to_string(),
remote_name: remote.to_string(),
})
.await
.context("sending pull request")?;
// TODO: wire through remote
Ok(())
}
}
}) })
} }
@@ -1227,49 +1143,66 @@ impl Repository {
path: &RepoPath, path: &RepoPath,
content: Option<String>, content: Option<String>,
) -> oneshot::Receiver<anyhow::Result<()>> { ) -> oneshot::Receiver<anyhow::Result<()>> {
self.send_message(Message::SetIndexText( let path = path.clone();
self.git_repo.clone(), self.send_job(|git_repo| async move {
path.clone(), match git_repo {
content, GitRepo::Local(repo) => repo.set_index_text(&path, content),
)) GitRepo::Remote {
} project_id,
client,
pub fn get_remotes(&self, branch_name: Option<String>, cx: &App) -> Task<Result<Vec<Remote>>> { worktree_id,
match self.git_repo.clone() { work_directory_id,
GitRepo::Local(git_repository) => { } => {
cx.background_spawn( client
async move { git_repository.get_remotes(branch_name.as_deref()) }, .request(proto::SetIndexText {
) project_id: project_id.0,
worktree_id: worktree_id.to_proto(),
work_directory_id: work_directory_id.to_proto(),
path: path.as_ref().to_proto(),
text: content,
})
.await?;
Ok(())
}
} }
GitRepo::Remote { })
project_id,
client,
worktree_id,
work_directory_id,
} => cx.background_spawn(async move {
let response = client
.request(proto::GetRemotes {
project_id: project_id.0,
worktree_id: worktree_id.to_proto(),
work_directory_id: work_directory_id.to_proto(),
branch_name,
})
.await?;
Ok(response
.remotes
.into_iter()
.map(|remotes| git::repository::Remote {
name: remotes.name.into(),
})
.collect())
}),
}
} }
fn send_message(&self, message: Message) -> oneshot::Receiver<anyhow::Result<()>> { pub fn get_remotes(
let (result_tx, result_rx) = futures::channel::oneshot::channel(); &self,
self.update_sender.unbounded_send((message, result_tx)).ok(); branch_name: Option<String>,
result_rx ) -> oneshot::Receiver<Result<Vec<Remote>>> {
self.send_job(|repo| async move {
match repo {
GitRepo::Local(git_repository) => {
git_repository.get_remotes(branch_name.as_deref())
}
GitRepo::Remote {
project_id,
client,
worktree_id,
work_directory_id,
} => {
let response = client
.request(proto::GetRemotes {
project_id: project_id.0,
worktree_id: worktree_id.to_proto(),
work_directory_id: work_directory_id.to_proto(),
branch_name,
})
.await?;
let remotes = response
.remotes
.into_iter()
.map(|remotes| git::repository::Remote {
name: remotes.name.into(),
})
.collect();
Ok(remotes)
}
}
})
} }
} }