History

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

Mikayla Maki committed 1 year ago

Commit 5edded51 parent 78da39e3 files · +365 −433Browse files

Unified · Split

3 files
  1. crates/git/src/repository.rs +6 −9
  2. crates/git_ui/src/git_panel.rs +6 −4
  3. crates/project/src/git.rs +353 −420
crates/git/src/repository.rs+6−9
@@ -618,10 +618,9 @@ impl GitRepository for RealGitRepository { · 617 unmodified lines
618618 "Failed to push:\n{}",
619619 String::from_utf8_lossy(&output.stderr)
620620 ));
621+ } else {
622+ Ok(())
621623 }
622−
623− // TODO: Get remote response out of this and show it to the user
624− Ok(())
625624 }
626625
627626 fn pull(&self, branch_name: &str, remote_name: &str) -> Result<()> {
@@ -639,10 +638,9 @@ impl GitRepository for RealGitRepository { · 11 unmodified lines
639638 "Failed to pull:\n{}",
640639 String::from_utf8_lossy(&output.stderr)
641640 ));
641+ } else {
642+ return Ok(());
642643 }
643−
644− // TODO: Get remote response out of this and show it to the user
645− Ok(())
646644 }
647645
648646 fn fetch(&self) -> Result<()> {
@@ -658,10 +656,9 @@ impl GitRepository for RealGitRepository { · 9 unmodified lines
658656 "Failed to fetch:\n{}",
659657 String::from_utf8_lossy(&output.stderr)
660658 ));
659+ } else {
660+ return Ok(());
661661 }
662−
663− // TODO: Get remote response out of this and show it to the user
664− Ok(())
665662 }
666663
667664 fn get_remotes(&self, branch_name: Option<&str>) -> Result<Vec<Remote>> {
crates/git_ui/src/git_panel.rs+6−4
@@ -1386,14 +1386,14 @@ impl GitPanel { · 1385 unmodified lines
13861386 };
13871387
13881388 let mut current_remotes: Vec<Remote> = repo
1389− .update(&mut cx, |repo, cx| {
1389+ .update(&mut cx, |repo, _| {
13901390 let Some(current_branch) = repo.current_branch() else {
13911391 return Err(anyhow::anyhow!("No active branch"));
13921392 };
13931393
1394− Ok(repo.get_remotes(Some(current_branch.name.to_string()), cx))
1394+ Ok(repo.get_remotes(Some(current_branch.name.to_string())))
13951395 })??
1396− .await?;
1396+ .await??;
13971397
13981398 if current_remotes.len() == 0 {
13991399 return Err(anyhow::anyhow!("No active remote"));
@@ -2357,7 +2357,9 @@ impl GitPanel { · 957 unmodified lines
23572357 let Some(repo) = self.active_repository.clone() else {
23582358 return Task::ready(Err(anyhow::anyhow!("no active repo")));
23592359 };
2360− repo.update(cx, |repo, cx| repo.show(sha, cx))
2360+
2361+ let show = repo.read(cx).show(sha);
2362+ cx.spawn(|_, _| async move { show.await? })
23612363 }
23622364
23632365 fn deploy_entry_context_menu(
crates/project/src/git.rs+353−420
@@ -11,26 +11,29 @@ use git::{ · 10 unmodified lines
1111 status::{GitSummary, TrackedSummary},
1212 };
1313 use gpui::{
14− App, AppContext as _, AsyncApp, Context, Entity, EventEmitter, SharedString, Subscription,
15− Task, WeakEntity,
14+ App, AppContext, AsyncApp, Context, Entity, EventEmitter, SharedString, Subscription, Task,
15+ WeakEntity,
1616 };
1717 use language::{Buffer, LanguageRegistry};
1818 use rpc::proto::{git_reset, ToProto};
1919 use rpc::{proto, AnyProtoClient, TypedEnvelope};
2020 use settings::WorktreeId;
21+use std::future::Future;
2122 use std::path::{Path, PathBuf};
2223 use std::sync::Arc;
2324 use text::BufferId;
2425 use util::{maybe, ResultExt};
2526 use worktree::{ProjectEntryId, RepositoryEntry, StatusEntry};
2627
28+type GitJob = Box<dyn FnOnce(&mut AsyncApp) -> Task<()>>;
29+
2730 pub struct GitStore {
2831 buffer_store: Entity<BufferStore>,
2932 pub(super) project_id: Option<ProjectId>,
3033 pub(super) client: Option<AnyProtoClient>,
3134 repositories: Vec<Entity<Repository>>,
3235 active_index: Option<usize>,
33− update_sender: mpsc::UnboundedSender<(Message, oneshot::Sender<Result<()>>)>,
36+ update_sender: mpsc::UnboundedSender<GitJob>,
3437 _subscription: Subscription,
3538 }
3639
@@ -41,7 +44,7 @@ pub struct Repository { · 4 unmodified lines
4144 pub repository_entry: RepositoryEntry,
4245 pub git_repo: GitRepo,
4346 pub merge_message: Option<String>,
44− update_sender: mpsc::UnboundedSender<(Message, oneshot::Sender<Result<()>>)>,
47+ job_sender: mpsc::UnboundedSender<GitJob>,
4548 }
4649
4750 #[derive(Clone)]
@@ -55,40 +58,6 @@ pub enum GitRepo { · 7 unmodified lines
5558 },
5659 }
5760
58−pub enum Message {
59− Commit {
60− git_repo: GitRepo,
61− message: SharedString,
62− name_and_email: Option<(SharedString, SharedString)>,
63− },
64− Reset {
65− repo: GitRepo,
66− commit: SharedString,
67− reset_mode: ResetMode,
68− },
69− CheckoutFiles {
70− repo: GitRepo,
71− commit: SharedString,
72− paths: Vec<RepoPath>,
73− },
74− Stage(GitRepo, Vec<RepoPath>),
75− Unstage(GitRepo, Vec<RepoPath>),
76− SetIndexText(GitRepo, RepoPath, Option<String>),
77− Push {
78− repo: GitRepo,
79− branch_name: SharedString,
80− remote_name: SharedString,
81− options: Option<PushOptions>,
82− },
83− Pull {
84− repo: GitRepo,
85− branch_name: SharedString,
86− remote_name: SharedString,
87− },
88− Fetch(GitRepo),
89−}
90−
91−#[derive(Debug)]
9261 pub enum GitEvent {
9362 ActiveRepositoryChanged,
9463 FileSystemUpdated,
@@ -220,7 +189,7 @@ impl GitStore { · 125 unmodified lines
220189 worktree_id,
221190 repository_entry: repo.clone(),
222191 git_repo,
223− update_sender: self.update_sender.clone(),
192+ job_sender: self.update_sender.clone(),
224193 merge_message,
225194 commit_message_buffer: None,
226195 })
@@ -252,272 +221,15 @@ impl GitStore { · 25 unmodified lines
252221 self.repositories.clone()
253222 }
254223
255− fn spawn_git_worker(
256− cx: &mut Context<'_, GitStore>,
257− ) -> mpsc::UnboundedSender<(Message, oneshot::Sender<Result<()>>)> {
258− let (update_sender, mut update_receiver) =
259− mpsc::unbounded::<(Message, oneshot::Sender<Result<()>>)>();
260− cx.spawn(|_, cx| async move {
261− while let Some((msg, respond)) = update_receiver.next().await {
262− if !respond.is_canceled() {
263− let result = cx.background_spawn(Self::process_git_msg(msg)).await;
264− respond.send(result).ok();
265− }
224+ fn spawn_git_worker(cx: &mut Context<'_, GitStore>) -> mpsc::UnboundedSender<GitJob> {
225+ let (job_tx, mut job_rx) = mpsc::unbounded::<GitJob>();
226+ cx.spawn(|_, mut cx| async move {
227+ while let Some(job) = job_rx.next().await {
228+ job(&mut cx).await
266229 }
267230 })
268231 .detach();
269− update_sender
270− }
271−
272− async fn process_git_msg(msg: Message) -> Result<()> {
273− match msg {
274− Message::Fetch(repo) => {
275− match repo {
276− GitRepo::Local(git_repository) => git_repository.fetch()?,
277− GitRepo::Remote {
278− project_id,
279− client,
280− worktree_id,
281− work_directory_id,
282− } => {
283− client
284− .request(proto::Fetch {
285− project_id: project_id.0,
286− worktree_id: worktree_id.to_proto(),
287− work_directory_id: work_directory_id.to_proto(),
288− })
289− .await
290− .context("sending fetch request")?;
291− }
292− }
293− Ok(())
294− }
295−
296− Message::Pull {
297− repo,
298− branch_name,
299− remote_name,
300− } => {
301− match repo {
302− GitRepo::Local(git_repository) => {
303− git_repository.pull(&branch_name, &remote_name)?
304− }
305− GitRepo::Remote {
306− project_id,
307− client,
308− worktree_id,
309− work_directory_id,
310− } => {
311− client
312− .request(proto::Pull {
313− project_id: project_id.0,
314− worktree_id: worktree_id.to_proto(),
315− work_directory_id: work_directory_id.to_proto(),
316− branch_name: branch_name.to_string(),
317− remote_name: remote_name.to_string(),
318− })
319− .await
320− .context("sending pull request")?;
321− }
322− }
323− Ok(())
324− }
325− Message::Push {
326− repo,
327− branch_name,
328− remote_name,
329− options,
330− } => {
331− match repo {
332− GitRepo::Local(git_repository) => {
333− git_repository.push(&branch_name, &remote_name, options)?
334− }
335− GitRepo::Remote {
336− project_id,
337− client,
338− worktree_id,
339− work_directory_id,
340− } => {
341− client
342− .request(proto::Push {
343− project_id: project_id.0,
344− worktree_id: worktree_id.to_proto(),
345− work_directory_id: work_directory_id.to_proto(),
346− branch_name: branch_name.to_string(),
347− remote_name: remote_name.to_string(),
348− options: options.map(|options| match options {
349− PushOptions::Force => proto::push::PushOptions::Force,
350− PushOptions::SetUpstream => {
351− proto::push::PushOptions::SetUpstream
352− }
353− }
354− as i32),
355− })
356− .await
357− .context("sending push request")?;
358− }
359− }
360− Ok(())
361− }
362− Message::Stage(repo, paths) => {
363− match repo {
364− GitRepo::Local(repo) => repo.stage_paths(&paths)?,
365− GitRepo::Remote {
366− project_id,
367− client,
368− worktree_id,
369− work_directory_id,
370− } => {
371− client
372− .request(proto::Stage {
373− project_id: project_id.0,
374− worktree_id: worktree_id.to_proto(),
375− work_directory_id: work_directory_id.to_proto(),
376− paths: paths
377− .into_iter()
378− .map(|repo_path| repo_path.as_ref().to_proto())
379− .collect(),
380− })
381− .await
382− .context("sending stage request")?;
383− }
384− }
385− Ok(())
386− }
387− Message::Reset {
388− repo,
389− commit,
390− reset_mode,
391− } => {
392− match repo {
393− GitRepo::Local(repo) => repo.reset(&commit, reset_mode)?,
394− GitRepo::Remote {
395− project_id,
396− client,
397− worktree_id,
398− work_directory_id,
399− } => {
400− client
401− .request(proto::GitReset {
402− project_id: project_id.0,
403− worktree_id: worktree_id.to_proto(),
404− work_directory_id: work_directory_id.to_proto(),
405− commit: commit.into(),
406− mode: match reset_mode {
407− ResetMode::Soft => git_reset::ResetMode::Soft.into(),
408− ResetMode::Mixed => git_reset::ResetMode::Mixed.into(),
409− },
410− })
411− .await?;
412− }
413− }
414− Ok(())
415− }
416−
417− Message::CheckoutFiles {
418− repo,
419− commit,
420− paths,
421− } => {
422− match repo {
423− GitRepo::Local(repo) => repo.checkout_files(&commit, &paths)?,
424− GitRepo::Remote {
425− project_id,
426− client,
427− worktree_id,
428− work_directory_id,
429− } => {
430− client
431− .request(proto::GitCheckoutFiles {
432− project_id: project_id.0,
433− worktree_id: worktree_id.to_proto(),
434− work_directory_id: work_directory_id.to_proto(),
435− commit: commit.into(),
436− paths: paths
437− .into_iter()
438− .map(|p| p.to_string_lossy().to_string())
439− .collect(),
440− })
441− .await?;
442− }
443− }
444− Ok(())
445− }
446− Message::Unstage(repo, paths) => {
447− match repo {
448− GitRepo::Local(repo) => repo.unstage_paths(&paths)?,
449− GitRepo::Remote {
450− project_id,
451− client,
452− worktree_id,
453− work_directory_id,
454− } => {
455− client
456− .request(proto::Unstage {
457− project_id: project_id.0,
458− worktree_id: worktree_id.to_proto(),
459− work_directory_id: work_directory_id.to_proto(),
460− paths: paths
461− .into_iter()
462− .map(|repo_path| repo_path.as_ref().to_proto())
463− .collect(),
464− })
465− .await
466− .context("sending unstage request")?;
467− }
468− }
469− Ok(())
470− }
471− Message::Commit {
472− git_repo,
473− message,
474− name_and_email,
475− } => {
476− match git_repo {
477− GitRepo::Local(repo) => repo.commit(
478− message.as_ref(),
479− name_and_email
480− .as_ref()
481− .map(|(name, email)| (name.as_ref(), email.as_ref())),
482− )?,
483− GitRepo::Remote {
484− project_id,
485− client,
486− worktree_id,
487− work_directory_id,
488− } => {
489− let (name, email) = name_and_email.unzip();
490− client
491− .request(proto::Commit {
492− project_id: project_id.0,
493− worktree_id: worktree_id.to_proto(),
494− work_directory_id: work_directory_id.to_proto(),
495− message: String::from(message),
496− name: name.map(String::from),
497− email: email.map(String::from),
498− })
499− .await
500− .context("sending commit request")?;
501− }
502− }
503− Ok(())
504− }
505− Message::SetIndexText(git_repo, path, text) => match git_repo {
506− GitRepo::Local(repo) => repo.set_index_text(&path, text),
507− GitRepo::Remote {
508− project_id,
509− client,
510− worktree_id,
511− work_directory_id,
512− } => client.send(proto::SetIndexText {
513− project_id: project_id.0,
514− worktree_id: worktree_id.to_proto(),
515− work_directory_id: work_directory_id.to_proto(),
516− path: path.as_ref().to_proto(),
517− text,
518− }),
519− },
520− }
232+ job_tx
521233 }
522234
523235 async fn handle_fetch(
@@ -696,10 +408,10 @@ impl GitStore { · 172 unmodified lines
696408 let branch_name = envelope.payload.branch_name;
697409
698410 let remotes = repository_handle
699− .update(&mut cx, |repository_handle, cx| {
700− repository_handle.get_remotes(branch_name, cx)
411+ .update(&mut cx, |repository_handle, _| {
412+ repository_handle.get_remotes(branch_name)
701413 })?
702− .await?;
414+ .await??;
703415
704416 Ok(proto::GetRemotesResponse {
705417 remotes: remotes
@@ -722,10 +434,10 @@ impl GitStore { · 16 unmodified lines
722434 Self::repository_for_request(&this, worktree_id, work_directory_id, &mut cx)?;
723435
724436 let commit = repository_handle
725− .update(&mut cx, |repository_handle, cx| {
726− repository_handle.show(&envelope.payload.commit, cx)
437+ .update(&mut cx, |repository_handle, _| {
438+ repository_handle.show(&envelope.payload.commit)
727439 })?
728− .await?;
440+ .await??;
729441 Ok(proto::GitCommitDetails {
730442 sha: commit.sha.into(),
731443 message: commit.message.into(),
@@ -854,6 +566,26 @@ impl Repository { · 122 unmodified lines
854566 self.repository_entry.branch()
855567 }
856568
569+ fn send_job<F, Fut, R>(&self, job: F) -> oneshot::Receiver<R>
570+ where
571+ F: FnOnce(GitRepo) -> Fut + 'static,
572+ Fut: Future<Output = R> + Send + 'static,
573+ R: Send + 'static,
574+ {
575+ let (result_tx, result_rx) = futures::channel::oneshot::channel();
576+ let git_repo = self.git_repo.clone();
577+ self.job_sender
578+ .unbounded_send(Box::new(|cx: &mut AsyncApp| {
579+ let job = job(git_repo);
580+ cx.background_spawn(async move {
581+ let result = job.await;
582+ result_tx.send(result).ok();
583+ })
584+ }))
585+ .ok();
586+ result_rx
587+ }
588+
857589 pub fn display_name(&self, project: &Project, cx: &App) -> SharedString {
858590 maybe!({
859591 let project_path = self.repo_path_to_project_path(&"".into())?;
@@ -1004,60 +736,106 @@ impl Repository { · 144 unmodified lines
1004736 commit: &str,
1005737 paths: Vec<RepoPath>,
1006738 ) -> oneshot::Receiver<Result<()>> {
1007− self.send_message(Message::CheckoutFiles {
1008− repo: self.git_repo.clone(),
1009− commit: commit.to_string().into(),
1010− paths,
739+ let commit = commit.to_string();
740+ self.send_job(|git_repo| async move {
741+ match git_repo {
742+ GitRepo::Local(repo) => repo.checkout_files(&commit, &paths),
743+ GitRepo::Remote {
744+ project_id,
745+ client,
746+ worktree_id,
747+ work_directory_id,
748+ } => {
749+ client
750+ .request(proto::GitCheckoutFiles {
751+ project_id: project_id.0,
752+ worktree_id: worktree_id.to_proto(),
753+ work_directory_id: work_directory_id.to_proto(),
754+ commit,
755+ paths: paths
756+ .into_iter()
757+ .map(|p| p.to_string_lossy().to_string())
758+ .collect(),
759+ })
760+ .await?;
761+
762+ Ok(())
763+ }
764+ }
1011765 })
1012766 }
1013767
1014768 pub fn reset(&self, commit: &str, reset_mode: ResetMode) -> oneshot::Receiver<Result<()>> {
1015− self.send_message(Message::Reset {
1016− repo: self.git_repo.clone(),
1017− commit: commit.to_string().into(),
1018− reset_mode,
769+ let commit = commit.to_string();
770+ self.send_job(|git_repo| async move {
771+ match git_repo {
772+ GitRepo::Local(git_repo) => git_repo.reset(&commit, reset_mode),
773+ GitRepo::Remote {
774+ project_id,
775+ client,
776+ worktree_id,
777+ work_directory_id,
778+ } => {
779+ client
780+ .request(proto::GitReset {
781+ project_id: project_id.0,
782+ worktree_id: worktree_id.to_proto(),
783+ work_directory_id: work_directory_id.to_proto(),
784+ commit,
785+ mode: match reset_mode {
786+ ResetMode::Soft => git_reset::ResetMode::Soft.into(),
787+ ResetMode::Mixed => git_reset::ResetMode::Mixed.into(),
788+ },
789+ })
790+ .await?;
791+
792+ Ok(())
793+ }
794+ }
1019795 })
1020796 }
1021797
1022− pub fn show(&self, commit: &str, cx: &Context<Self>) -> Task<Result<CommitDetails>> {
798+ pub fn show(&self, commit: &str) -> oneshot::Receiver<Result<CommitDetails>> {
1023799 let commit = commit.to_string();
1024− match self.git_repo.clone() {
1025− GitRepo::Local(git_repository) => {
1026− let commit = commit.to_string();
1027− cx.background_spawn(async move { git_repository.show(&commit) })
1028− }
1029− GitRepo::Remote {
1030− project_id,
1031− client,
1032− worktree_id,
1033− work_directory_id,
1034− } => cx.background_spawn(async move {
1035− let resp = client
1036− .request(proto::GitShow {
1037− project_id: project_id.0,
1038− worktree_id: worktree_id.to_proto(),
1039− work_directory_id: work_directory_id.to_proto(),
1040− commit,
800+ self.send_job(|git_repo| async move {
801+ match git_repo {
802+ GitRepo::Local(git_repository) => git_repository.show(&commit),
803+ GitRepo::Remote {
804+ project_id,
805+ client,
806+ worktree_id,
807+ work_directory_id,
808+ } => {
809+ let resp = client
810+ .request(proto::GitShow {
811+ project_id: project_id.0,
812+ worktree_id: worktree_id.to_proto(),
813+ work_directory_id: work_directory_id.to_proto(),
814+ commit,
815+ })
816+ .await?;
817+
818+ Ok(CommitDetails {
819+ sha: resp.sha.into(),
820+ message: resp.message.into(),
821+ commit_timestamp: resp.commit_timestamp,
822+ committer_email: resp.committer_email.into(),
823+ committer_name: resp.committer_name.into(),
1041824 })
1042− .await?;
1043−
1044− Ok(CommitDetails {
1045− sha: resp.sha.into(),
1046− message: resp.message.into(),
1047− commit_timestamp: resp.commit_timestamp,
1048− committer_email: resp.committer_email.into(),
1049− committer_name: resp.committer_name.into(),
1050− })
1051− }),
1052− }
825+ }
826+ }
827+ })
1053828 }
1054829
1055830 fn buffer_store(&self, cx: &App) -> Option<Entity<BufferStore>> {
1056831 Some(self.git_store.upgrade()?.read(cx).buffer_store.clone())
1057832 }
1058833
1059− pub fn stage_entries(&self, entries: Vec<RepoPath>, cx: &mut App) -> Task<anyhow::Result<()>> {
1060− let (result_tx, result_rx) = futures::channel::oneshot::channel();
834+ pub fn stage_entries(
835+ &self,
836+ entries: Vec<RepoPath>,
837+ cx: &mut Context<Self>,
838+ ) -> Task<anyhow::Result<()>> {
1061839 if entries.is_empty() {
1062840 return Task::ready(Ok(()));
1063841 }
@@ -1083,16 +861,41 @@ impl Repository { · 19 unmodified lines
1083861 })
1084862 }
1085863
1086− let update_sender = self.update_sender.clone();
1087− let git_repo = self.git_repo.clone();
1088− cx.spawn(|_| async move {
864+ cx.spawn(|this, mut cx| async move {
1089865 for save_future in save_futures {
1090866 save_future.await?;
1091867 }
1092− update_sender
1093− .unbounded_send((Message::Stage(git_repo, entries), result_tx))
1094− .ok();
1095− result_rx.await.anyhow()??;
868+
869+ this.update(&mut cx, |this, _| {
870+ this.send_job(|git_repo| async move {
871+ match git_repo {
872+ GitRepo::Local(repo) => repo.stage_paths(&entries),
873+ GitRepo::Remote {
874+ project_id,
875+ client,
876+ worktree_id,
877+ work_directory_id,
878+ } => {
879+ client
880+ .request(proto::Stage {
881+ project_id: project_id.0,
882+ worktree_id: worktree_id.to_proto(),
883+ work_directory_id: work_directory_id.to_proto(),
884+ paths: entries
885+ .into_iter()
886+ .map(|repo_path| repo_path.as_ref().to_proto())
887+ .collect(),
888+ })
889+ .await
890+ .context("sending stage request")?;
891+
892+ Ok(())
893+ }
894+ }
895+ })
896+ })?
897+ .await??;
898+
1096899 Ok(())
1097900 })
1098901 }
@@ -1100,9 +903,8 @@ impl Repository { · 1 unmodified line
1100903 pub fn unstage_entries(
1101904 &self,
1102905 entries: Vec<RepoPath>,
1103− cx: &mut App,
906+ cx: &mut Context<Self>,
1104907 ) -> Task<anyhow::Result<()>> {
1105− let (result_tx, result_rx) = futures::channel::oneshot::channel();
1106908 if entries.is_empty() {
1107909 return Task::ready(Ok(()));
1108910 }
@@ -1128,21 +930,46 @@ impl Repository { · 19 unmodified lines
1128930 })
1129931 }
1130932
1131− let update_sender = self.update_sender.clone();
1132− let git_repo = self.git_repo.clone();
1133− cx.spawn(|_| async move {
933+ cx.spawn(move |this, mut cx| async move {
1134934 for save_future in save_futures {
1135935 save_future.await?;
1136936 }
1137− update_sender
1138− .unbounded_send((Message::Unstage(git_repo, entries), result_tx))
1139− .ok();
1140− result_rx.await.anyhow()??;
937+
938+ this.update(&mut cx, |this, _| {
939+ this.send_job(|git_repo| async move {
940+ match git_repo {
941+ GitRepo::Local(repo) => repo.unstage_paths(&entries),
942+ GitRepo::Remote {
943+ project_id,
944+ client,
945+ worktree_id,
946+ work_directory_id,
947+ } => {
948+ client
949+ .request(proto::Unstage {
950+ project_id: project_id.0,
951+ worktree_id: worktree_id.to_proto(),
952+ work_directory_id: work_directory_id.to_proto(),
953+ paths: entries
954+ .into_iter()
955+ .map(|repo_path| repo_path.as_ref().to_proto())
956+ .collect(),
957+ })
958+ .await
959+ .context("sending unstage request")?;
960+
961+ Ok(())
962+ }
963+ }
964+ })
965+ })?
966+ .await??;
967+
1141968 Ok(())
1142969 })
1143970 }
1144971
1145− pub fn stage_all(&self, cx: &mut App) -> Task<anyhow::Result<()>> {
972+ pub fn stage_all(&self, cx: &mut Context<Self>) -> Task<anyhow::Result<()>> {
1146973 let to_stage = self
1147974 .repository_entry
1148975 .status()
@@ -1152,7 +979,7 @@ impl Repository { · 3 unmodified lines
1152979 self.stage_entries(to_stage, cx)
1153980 }
1154981
1155− pub fn unstage_all(&self, cx: &mut App) -> Task<anyhow::Result<()>> {
982+ pub fn unstage_all(&self, cx: &mut Context<Self>) -> Task<anyhow::Result<()>> {
1156983 let to_unstage = self
1157984 .repository_entry
1158985 .status()
@@ -1185,15 +1012,62 @@ impl Repository { · 26 unmodified lines
11851012 message: SharedString,
11861013 name_and_email: Option<(SharedString, SharedString)>,
11871014 ) -> oneshot::Receiver<Result<()>> {
1188− self.send_message(Message::Commit {
1189− git_repo: self.git_repo.clone(),
1190− message,
1191− name_and_email,
1015+ self.send_job(|git_repo| async move {
1016+ match git_repo {
1017+ GitRepo::Local(repo) => repo.commit(
1018+ message.as_ref(),
1019+ name_and_email
1020+ .as_ref()
1021+ .map(|(name, email)| (name.as_ref(), email.as_ref())),
1022+ ),
1023+ GitRepo::Remote {
1024+ project_id,
1025+ client,
1026+ worktree_id,
1027+ work_directory_id,
1028+ } => {
1029+ let (name, email) = name_and_email.unzip();
1030+ client
1031+ .request(proto::Commit {
1032+ project_id: project_id.0,
1033+ worktree_id: worktree_id.to_proto(),
1034+ work_directory_id: work_directory_id.to_proto(),
1035+ message: String::from(message),
1036+ name: name.map(String::from),
1037+ email: email.map(String::from),
1038+ })
1039+ .await
1040+ .context("sending commit request")?;
1041+
1042+ Ok(())
1043+ }
1044+ }
11921045 })
11931046 }
11941047
11951048 pub fn fetch(&self) -> oneshot::Receiver<Result<()>> {
1196− self.send_message(Message::Fetch(self.git_repo.clone()))
1049+ self.send_job(|git_repo| async move {
1050+ match git_repo {
1051+ GitRepo::Local(git_repository) => git_repository.fetch(),
1052+ GitRepo::Remote {
1053+ project_id,
1054+ client,
1055+ worktree_id,
1056+ work_directory_id,
1057+ } => {
1058+ client
1059+ .request(proto::Fetch {
1060+ project_id: project_id.0,
1061+ worktree_id: worktree_id.to_proto(),
1062+ work_directory_id: work_directory_id.to_proto(),
1063+ })
1064+ .await
1065+ .context("sending fetch request")?;
1066+
1067+ Ok(())
1068+ }
1069+ }
1070+ })
11971071 }
11981072
11991073 pub fn push(
@@ -1202,11 +1076,33 @@ impl Repository { · 2 unmodified lines
12021076 remote: SharedString,
12031077 options: Option<PushOptions>,
12041078 ) -> oneshot::Receiver<Result<()>> {
1205− self.send_message(Message::Push {
1206− repo: self.git_repo.clone(),
1207− branch_name: branch,
1208− remote_name: remote,
1209− options,
1079+ self.send_job(move |git_repo| async move {
1080+ match git_repo {
1081+ GitRepo::Local(git_repository) => git_repository.push(&branch, &remote, options),
1082+ GitRepo::Remote {
1083+ project_id,
1084+ client,
1085+ worktree_id,
1086+ work_directory_id,
1087+ } => {
1088+ client
1089+ .request(proto::Push {
1090+ project_id: project_id.0,
1091+ worktree_id: worktree_id.to_proto(),
1092+ work_directory_id: work_directory_id.to_proto(),
1093+ branch_name: branch.to_string(),
1094+ remote_name: remote.to_string(),
1095+ options: options.map(|options| match options {
1096+ PushOptions::Force => proto::push::PushOptions::Force,
1097+ PushOptions::SetUpstream => proto::push::PushOptions::SetUpstream,
1098+ } as i32),
1099+ })
1100+ .await
1101+ .context("sending push request")?;
1102+
1103+ Ok(())
1104+ }
1105+ }
12101106 })
12111107 }
12121108
@@ -1215,10 +1111,30 @@ impl Repository { · 2 unmodified lines
12151111 branch: SharedString,
12161112 remote: SharedString,
12171113 ) -> oneshot::Receiver<Result<()>> {
1218− self.send_message(Message::Pull {
1219− repo: self.git_repo.clone(),
1220− branch_name: branch,
1221− remote_name: remote,
1114+ self.send_job(|git_repo| async move {
1115+ match git_repo {
1116+ GitRepo::Local(git_repository) => git_repository.pull(&branch, &remote),
1117+ GitRepo::Remote {
1118+ project_id,
1119+ client,
1120+ worktree_id,
1121+ work_directory_id,
1122+ } => {
1123+ client
1124+ .request(proto::Pull {
1125+ project_id: project_id.0,
1126+ worktree_id: worktree_id.to_proto(),
1127+ work_directory_id: work_directory_id.to_proto(),
1128+ branch_name: branch.to_string(),
1129+ remote_name: remote.to_string(),
1130+ })
1131+ .await
1132+ .context("sending pull request")?;
1133+
1134+ // TODO: wire through remote
1135+ Ok(())
1136+ }
1137+ }
12221138 })
12231139 }
12241140
@@ -1227,49 +1143,66 @@ impl Repository { · 2 unmodified lines
12271143 path: &RepoPath,
12281144 content: Option<String>,
12291145 ) -> oneshot::Receiver<anyhow::Result<()>> {
1230− self.send_message(Message::SetIndexText(
1231− self.git_repo.clone(),
1232− path.clone(),
1233− content,
1234− ))
1235− }
1236−
1237− pub fn get_remotes(&self, branch_name: Option<String>, cx: &App) -> Task<Result<Vec<Remote>>> {
1238− match self.git_repo.clone() {
1239− GitRepo::Local(git_repository) => {
1240− cx.background_spawn(
1241− async move { git_repository.get_remotes(branch_name.as_deref()) },
1242− )
1146+ let path = path.clone();
1147+ self.send_job(|git_repo| async move {
1148+ match git_repo {
1149+ GitRepo::Local(repo) => repo.set_index_text(&path, content),
1150+ GitRepo::Remote {
1151+ project_id,
1152+ client,
1153+ worktree_id,
1154+ work_directory_id,
1155+ } => {
1156+ client
1157+ .request(proto::SetIndexText {
1158+ project_id: project_id.0,
1159+ worktree_id: worktree_id.to_proto(),
1160+ work_directory_id: work_directory_id.to_proto(),
1161+ path: path.as_ref().to_proto(),
1162+ text: content,
1163+ })
1164+ .await?;
1165+ Ok(())
1166+ }
12431167 }
1244− GitRepo::Remote {
1245− project_id,
1246− client,
1247− worktree_id,
1248− work_directory_id,
1249− } => cx.background_spawn(async move {
1250− let response = client
1251− .request(proto::GetRemotes {
1252− project_id: project_id.0,
1253− worktree_id: worktree_id.to_proto(),
1254− work_directory_id: work_directory_id.to_proto(),
1255− branch_name,
1256− })
1257− .await?;
1258−
1259− Ok(response
1260− .remotes
1261− .into_iter()
1262− .map(|remotes| git::repository::Remote {
1263− name: remotes.name.into(),
1264− })
1265− .collect())
1266− }),
1267− }
1168+ })
12681169 }
12691170
1270− fn send_message(&self, message: Message) -> oneshot::Receiver<anyhow::Result<()>> {
1271− let (result_tx, result_rx) = futures::channel::oneshot::channel();
1272− self.update_sender.unbounded_send((message, result_tx)).ok();
1273− result_rx
1171+ pub fn get_remotes(
1172+ &self,
1173+ branch_name: Option<String>,
1174+ ) -> oneshot::Receiver<Result<Vec<Remote>>> {
1175+ self.send_job(|repo| async move {
1176+ match repo {
1177+ GitRepo::Local(git_repository) => {
1178+ git_repository.get_remotes(branch_name.as_deref())
1179+ }
1180+ GitRepo::Remote {
1181+ project_id,
1182+ client,
1183+ worktree_id,
1184+ work_directory_id,
1185+ } => {
1186+ let response = client
1187+ .request(proto::GetRemotes {
1188+ project_id: project_id.0,
1189+ worktree_id: worktree_id.to_proto(),
1190+ work_directory_id: work_directory_id.to_proto(),
1191+ branch_name,
1192+ })
1193+ .await?;
1194+
1195+ let remotes = response
1196+ .remotes
1197+ .into_iter()
1198+ .map(|remotes| git::repository::Remote {
1199+ name: remotes.name.into(),
1200+ })
1201+ .collect();
1202+
1203+ Ok(remotes)
1204+ }
1205+ }
1206+ })
12741207 }
12751208 }