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
618 "Failed to push:\n{}",618 "Failed to push:\n{}",
619 String::from_utf8_lossy(&output.stderr)619 String::from_utf8_lossy(&output.stderr)
620 ));620 ));
621 } else {
622 Ok(())
621 }623 }
622
623 // TODO: Get remote response out of this and show it to the user
624 Ok(())
625 }624 }
626 625
627 fn pull(&self, branch_name: &str, remote_name: &str) -> Result<()> {626 fn pull(&self, branch_name: &str, remote_name: &str) -> Result<()> {
@@ -639,10 +638,9 @@ impl GitRepository for RealGitRepository { · 11 unmodified lines
639 "Failed to pull:\n{}",638 "Failed to pull:\n{}",
640 String::from_utf8_lossy(&output.stderr)639 String::from_utf8_lossy(&output.stderr)
641 ));640 ));
641 } else {
642 return Ok(());
642 }643 }
643
644 // TODO: Get remote response out of this and show it to the user
645 Ok(())
646 }644 }
647 645
648 fn fetch(&self) -> Result<()> {646 fn fetch(&self) -> Result<()> {
@@ -658,10 +656,9 @@ impl GitRepository for RealGitRepository { · 9 unmodified lines
658 "Failed to fetch:\n{}",656 "Failed to fetch:\n{}",
659 String::from_utf8_lossy(&output.stderr)657 String::from_utf8_lossy(&output.stderr)
660 ));658 ));
659 } else {
660 return Ok(());
661 }661 }
662
663 // TODO: Get remote response out of this and show it to the user
664 Ok(())
665 }662 }
666 663
667 fn get_remotes(&self, branch_name: Option<&str>) -> Result<Vec<Remote>> {664 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
1386 };1386 };
1387 1387
1388 let mut current_remotes: Vec<Remote> = repo1388 let mut current_remotes: Vec<Remote> = repo
1389 .update(&mut cx, |repo, cx| {1389 .update(&mut cx, |repo, _| {
1390 let Some(current_branch) = repo.current_branch() else {1390 let Some(current_branch) = repo.current_branch() else {
1391 return Err(anyhow::anyhow!("No active branch"));1391 return Err(anyhow::anyhow!("No active branch"));
1392 };1392 };
1393 1393
1394 Ok(repo.get_remotes(Some(current_branch.name.to_string()), cx))1394 Ok(repo.get_remotes(Some(current_branch.name.to_string())))
1395 })??1395 })??
1396 .await?;1396 .await??;
1397 1397
1398 if current_remotes.len() == 0 {1398 if current_remotes.len() == 0 {
1399 return Err(anyhow::anyhow!("No active remote"));1399 return Err(anyhow::anyhow!("No active remote"));
@@ -2357,7 +2357,9 @@ impl GitPanel { · 957 unmodified lines
2357 let Some(repo) = self.active_repository.clone() else {2357 let Some(repo) = self.active_repository.clone() else {
2358 return Task::ready(Err(anyhow::anyhow!("no active repo")));2358 return Task::ready(Err(anyhow::anyhow!("no active repo")));
2359 };2359 };
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? })
2361 }2363 }
2362 2364
2363 fn deploy_entry_context_menu(2365 fn deploy_entry_context_menu(
crates/project/src/git.rs+353−420
@@ -11,26 +11,29 @@ use git::{ · 10 unmodified lines
11 status::{GitSummary, TrackedSummary},11 status::{GitSummary, TrackedSummary},
12};12};
13use gpui::{13use gpui::{
14 App, AppContext as _, AsyncApp, Context, Entity, EventEmitter, SharedString, Subscription,14 App, AppContext, AsyncApp, Context, Entity, EventEmitter, SharedString, Subscription, Task,
15 Task, WeakEntity,15 WeakEntity,
16};16};
17use language::{Buffer, LanguageRegistry};17use language::{Buffer, LanguageRegistry};
18use rpc::proto::{git_reset, ToProto};18use rpc::proto::{git_reset, ToProto};
19use rpc::{proto, AnyProtoClient, TypedEnvelope};19use rpc::{proto, AnyProtoClient, TypedEnvelope};
20use settings::WorktreeId;20use settings::WorktreeId;
21use std::future::Future;
21use std::path::{Path, PathBuf};22use std::path::{Path, PathBuf};
22use std::sync::Arc;23use std::sync::Arc;
23use text::BufferId;24use text::BufferId;
24use util::{maybe, ResultExt};25use util::{maybe, ResultExt};
25use worktree::{ProjectEntryId, RepositoryEntry, StatusEntry};26use worktree::{ProjectEntryId, RepositoryEntry, StatusEntry};
26 27
28type GitJob = Box<dyn FnOnce(&mut AsyncApp) -> Task<()>>;
29
27pub struct GitStore {30pub struct GitStore {
28 buffer_store: Entity<BufferStore>,31 buffer_store: Entity<BufferStore>,
29 pub(super) project_id: Option<ProjectId>,32 pub(super) project_id: Option<ProjectId>,
30 pub(super) client: Option<AnyProtoClient>,33 pub(super) client: Option<AnyProtoClient>,
31 repositories: Vec<Entity<Repository>>,34 repositories: Vec<Entity<Repository>>,
32 active_index: Option<usize>,35 active_index: Option<usize>,
33 update_sender: mpsc::UnboundedSender<(Message, oneshot::Sender<Result<()>>)>,36 update_sender: mpsc::UnboundedSender<GitJob>,
34 _subscription: Subscription,37 _subscription: Subscription,
35}38}
36 39
@@ -41,7 +44,7 @@ pub struct Repository { · 4 unmodified lines
41 pub repository_entry: RepositoryEntry,44 pub repository_entry: RepositoryEntry,
42 pub git_repo: GitRepo,45 pub git_repo: GitRepo,
43 pub merge_message: Option<String>,46 pub merge_message: Option<String>,
44 update_sender: mpsc::UnboundedSender<(Message, oneshot::Sender<Result<()>>)>,47 job_sender: mpsc::UnboundedSender<GitJob>,
45}48}
46 49
47#[derive(Clone)]50#[derive(Clone)]
@@ -55,40 +58,6 @@ pub enum GitRepo { · 7 unmodified lines
55 },58 },
56}59}
57 60
58pub 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)]
92pub enum GitEvent {61pub enum GitEvent {
93 ActiveRepositoryChanged,62 ActiveRepositoryChanged,
94 FileSystemUpdated,63 FileSystemUpdated,
@@ -220,7 +189,7 @@ impl GitStore { · 125 unmodified lines
220 worktree_id,189 worktree_id,
221 repository_entry: repo.clone(),190 repository_entry: repo.clone(),
222 git_repo,191 git_repo,
223 update_sender: self.update_sender.clone(),192 job_sender: self.update_sender.clone(),
224 merge_message,193 merge_message,
225 commit_message_buffer: None,194 commit_message_buffer: None,
226 })195 })
@@ -252,272 +221,15 @@ impl GitStore { · 25 unmodified lines
252 self.repositories.clone()221 self.repositories.clone()
253 }222 }
254 223
255 fn spawn_git_worker(224 fn spawn_git_worker(cx: &mut Context<'_, GitStore>) -> mpsc::UnboundedSender<GitJob> {
256 cx: &mut Context<'_, GitStore>,225 let (job_tx, mut job_rx) = mpsc::unbounded::<GitJob>();
257 ) -> mpsc::UnboundedSender<(Message, oneshot::Sender<Result<()>>)> {226 cx.spawn(|_, mut cx| async move {
258 let (update_sender, mut update_receiver) =227 while let Some(job) = job_rx.next().await {
259 mpsc::unbounded::<(Message, oneshot::Sender<Result<()>>)>();228 job(&mut cx).await
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 }
266 }229 }
267 })230 })
268 .detach();231 .detach();
269 update_sender232 job_tx
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 }
521 }233 }
522 234
523 async fn handle_fetch(235 async fn handle_fetch(
@@ -696,10 +408,10 @@ impl GitStore { · 172 unmodified lines
696 let branch_name = envelope.payload.branch_name;408 let branch_name = envelope.payload.branch_name;
697 409
698 let remotes = repository_handle410 let remotes = repository_handle
699 .update(&mut cx, |repository_handle, cx| {411 .update(&mut cx, |repository_handle, _| {
700 repository_handle.get_remotes(branch_name, cx)412 repository_handle.get_remotes(branch_name)
701 })?413 })?
702 .await?;414 .await??;
703 415
704 Ok(proto::GetRemotesResponse {416 Ok(proto::GetRemotesResponse {
705 remotes: remotes417 remotes: remotes
@@ -722,10 +434,10 @@ impl GitStore { · 16 unmodified lines
722 Self::repository_for_request(&this, worktree_id, work_directory_id, &mut cx)?;434 Self::repository_for_request(&this, worktree_id, work_directory_id, &mut cx)?;
723 435
724 let commit = repository_handle436 let commit = repository_handle
725 .update(&mut cx, |repository_handle, cx| {437 .update(&mut cx, |repository_handle, _| {
726 repository_handle.show(&envelope.payload.commit, cx)438 repository_handle.show(&envelope.payload.commit)
727 })?439 })?
728 .await?;440 .await??;
729 Ok(proto::GitCommitDetails {441 Ok(proto::GitCommitDetails {
730 sha: commit.sha.into(),442 sha: commit.sha.into(),
731 message: commit.message.into(),443 message: commit.message.into(),
@@ -854,6 +566,26 @@ impl Repository { · 122 unmodified lines
854 self.repository_entry.branch()566 self.repository_entry.branch()
855 }567 }
856 568
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
857 pub fn display_name(&self, project: &Project, cx: &App) -> SharedString {589 pub fn display_name(&self, project: &Project, cx: &App) -> SharedString {
858 maybe!({590 maybe!({
859 let project_path = self.repo_path_to_project_path(&"".into())?;591 let project_path = self.repo_path_to_project_path(&"".into())?;
@@ -1004,60 +736,106 @@ impl Repository { · 144 unmodified lines
1004 commit: &str,736 commit: &str,
1005 paths: Vec<RepoPath>,737 paths: Vec<RepoPath>,
1006 ) -> oneshot::Receiver<Result<()>> {738 ) -> oneshot::Receiver<Result<()>> {
1007 self.send_message(Message::CheckoutFiles {739 let commit = commit.to_string();
1008 repo: self.git_repo.clone(),740 self.send_job(|git_repo| async move {
1009 commit: commit.to_string().into(),741 match git_repo {
1010 paths,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 }
1011 })765 })
1012 }766 }
1013 767
1014 pub fn reset(&self, commit: &str, reset_mode: ResetMode) -> oneshot::Receiver<Result<()>> {768 pub fn reset(&self, commit: &str, reset_mode: ResetMode) -> oneshot::Receiver<Result<()>> {
1015 self.send_message(Message::Reset {769 let commit = commit.to_string();
1016 repo: self.git_repo.clone(),770 self.send_job(|git_repo| async move {
1017 commit: commit.to_string().into(),771 match git_repo {
1018 reset_mode,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 }
1019 })795 })
1020 }796 }
1021 797
1022 pub fn show(&self, commit: &str, cx: &Context<Self>) -> Task<Result<CommitDetails>> {798 pub fn show(&self, commit: &str) -> oneshot::Receiver<Result<CommitDetails>> {
1023 let commit = commit.to_string();799 let commit = commit.to_string();
1024 match self.git_repo.clone() {800 self.send_job(|git_repo| async move {
1025 GitRepo::Local(git_repository) => {801 match git_repo {
1026 let commit = commit.to_string();802 GitRepo::Local(git_repository) => git_repository.show(&commit),
1027 cx.background_spawn(async move { git_repository.show(&commit) })803 GitRepo::Remote {
1028 }804 project_id,
1029 GitRepo::Remote {805 client,
1030 project_id,806 worktree_id,
1031 client,807 work_directory_id,
1032 worktree_id,808 } => {
1033 work_directory_id,809 let resp = client
1034 } => cx.background_spawn(async move {810 .request(proto::GitShow {
1035 let resp = client811 project_id: project_id.0,
1036 .request(proto::GitShow {812 worktree_id: worktree_id.to_proto(),
1037 project_id: project_id.0,813 work_directory_id: work_directory_id.to_proto(),
1038 worktree_id: worktree_id.to_proto(),814 commit,
1039 work_directory_id: work_directory_id.to_proto(),815 })
1040 commit,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(),
1041 })824 })
1042 .await?;825 }
1043 826 }
1044 Ok(CommitDetails {827 })
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 }
1053 }828 }
1054 829
1055 fn buffer_store(&self, cx: &App) -> Option<Entity<BufferStore>> {830 fn buffer_store(&self, cx: &App) -> Option<Entity<BufferStore>> {
1056 Some(self.git_store.upgrade()?.read(cx).buffer_store.clone())831 Some(self.git_store.upgrade()?.read(cx).buffer_store.clone())
1057 }832 }
1058 833
1059 pub fn stage_entries(&self, entries: Vec<RepoPath>, cx: &mut App) -> Task<anyhow::Result<()>> {834 pub fn stage_entries(
1060 let (result_tx, result_rx) = futures::channel::oneshot::channel();835 &self,
836 entries: Vec<RepoPath>,
837 cx: &mut Context<Self>,
838 ) -> Task<anyhow::Result<()>> {
1061 if entries.is_empty() {839 if entries.is_empty() {
1062 return Task::ready(Ok(()));840 return Task::ready(Ok(()));
1063 }841 }
@@ -1083,16 +861,41 @@ impl Repository { · 19 unmodified lines
1083 })861 })
1084 }862 }
1085 863
1086 let update_sender = self.update_sender.clone();864 cx.spawn(|this, mut cx| async move {
1087 let git_repo = self.git_repo.clone();
1088 cx.spawn(|_| async move {
1089 for save_future in save_futures {865 for save_future in save_futures {
1090 save_future.await?;866 save_future.await?;
1091 }867 }
1092 update_sender868
1093 .unbounded_send((Message::Stage(git_repo, entries), result_tx))869 this.update(&mut cx, |this, _| {
1094 .ok();870 this.send_job(|git_repo| async move {
1095 result_rx.await.anyhow()??;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
1096 Ok(())899 Ok(())
1097 })900 })
1098 }901 }
@@ -1100,9 +903,8 @@ impl Repository { · 1 unmodified line
1100 pub fn unstage_entries(903 pub fn unstage_entries(
1101 &self,904 &self,
1102 entries: Vec<RepoPath>,905 entries: Vec<RepoPath>,
1103 cx: &mut App,906 cx: &mut Context<Self>,
1104 ) -> Task<anyhow::Result<()>> {907 ) -> Task<anyhow::Result<()>> {
1105 let (result_tx, result_rx) = futures::channel::oneshot::channel();
1106 if entries.is_empty() {908 if entries.is_empty() {
1107 return Task::ready(Ok(()));909 return Task::ready(Ok(()));
1108 }910 }
@@ -1128,21 +930,46 @@ impl Repository { · 19 unmodified lines
1128 })930 })
1129 }931 }
1130 932
1131 let update_sender = self.update_sender.clone();933 cx.spawn(move |this, mut cx| async move {
1132 let git_repo = self.git_repo.clone();
1133 cx.spawn(|_| async move {
1134 for save_future in save_futures {934 for save_future in save_futures {
1135 save_future.await?;935 save_future.await?;
1136 }936 }
1137 update_sender937
1138 .unbounded_send((Message::Unstage(git_repo, entries), result_tx))938 this.update(&mut cx, |this, _| {
1139 .ok();939 this.send_job(|git_repo| async move {
1140 result_rx.await.anyhow()??;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
1141 Ok(())968 Ok(())
1142 })969 })
1143 }970 }
1144 971
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<()>> {
1146 let to_stage = self973 let to_stage = self
1147 .repository_entry974 .repository_entry
1148 .status()975 .status()
@@ -1152,7 +979,7 @@ impl Repository { · 3 unmodified lines
1152 self.stage_entries(to_stage, cx)979 self.stage_entries(to_stage, cx)
1153 }980 }
1154 981
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<()>> {
1156 let to_unstage = self983 let to_unstage = self
1157 .repository_entry984 .repository_entry
1158 .status()985 .status()
@@ -1185,15 +1012,62 @@ impl Repository { · 26 unmodified lines
1185 message: SharedString,1012 message: SharedString,
1186 name_and_email: Option<(SharedString, SharedString)>,1013 name_and_email: Option<(SharedString, SharedString)>,
1187 ) -> oneshot::Receiver<Result<()>> {1014 ) -> oneshot::Receiver<Result<()>> {
1188 self.send_message(Message::Commit {1015 self.send_job(|git_repo| async move {
1189 git_repo: self.git_repo.clone(),1016 match git_repo {
1190 message,1017 GitRepo::Local(repo) => repo.commit(
1191 name_and_email,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 }
1192 })1045 })
1193 }1046 }
1194 1047
1195 pub fn fetch(&self) -> oneshot::Receiver<Result<()>> {1048 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 })
1197 }1071 }
1198 1072
1199 pub fn push(1073 pub fn push(
@@ -1202,11 +1076,33 @@ impl Repository { · 2 unmodified lines
1202 remote: SharedString,1076 remote: SharedString,
1203 options: Option<PushOptions>,1077 options: Option<PushOptions>,
1204 ) -> oneshot::Receiver<Result<()>> {1078 ) -> oneshot::Receiver<Result<()>> {
1205 self.send_message(Message::Push {1079 self.send_job(move |git_repo| async move {
1206 repo: self.git_repo.clone(),1080 match git_repo {
1207 branch_name: branch,1081 GitRepo::Local(git_repository) => git_repository.push(&branch, &remote, options),
1208 remote_name: remote,1082 GitRepo::Remote {
1209 options,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 }
1210 })1106 })
1211 }1107 }
1212 1108
@@ -1215,10 +1111,30 @@ impl Repository { · 2 unmodified lines
1215 branch: SharedString,1111 branch: SharedString,
1216 remote: SharedString,1112 remote: SharedString,
1217 ) -> oneshot::Receiver<Result<()>> {1113 ) -> oneshot::Receiver<Result<()>> {
1218 self.send_message(Message::Pull {1114 self.send_job(|git_repo| async move {
1219 repo: self.git_repo.clone(),1115 match git_repo {
1220 branch_name: branch,1116 GitRepo::Local(git_repository) => git_repository.pull(&branch, &remote),
1221 remote_name: 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 }
1222 })1138 })
1223 }1139 }
1224 1140
@@ -1227,49 +1143,66 @@ impl Repository { · 2 unmodified lines
1227 path: &RepoPath,1143 path: &RepoPath,
1228 content: Option<String>,1144 content: Option<String>,
1229 ) -> oneshot::Receiver<anyhow::Result<()>> {1145 ) -> oneshot::Receiver<anyhow::Result<()>> {
1230 self.send_message(Message::SetIndexText(1146 let path = path.clone();
1231 self.git_repo.clone(),1147 self.send_job(|git_repo| async move {
1232 path.clone(),1148 match git_repo {
1233 content,1149 GitRepo::Local(repo) => repo.set_index_text(&path, content),
1234 ))1150 GitRepo::Remote {
1235 }1151 project_id,
1236 1152 client,
1237 pub fn get_remotes(&self, branch_name: Option<String>, cx: &App) -> Task<Result<Vec<Remote>>> {1153 worktree_id,
1238 match self.git_repo.clone() {1154 work_directory_id,
1239 GitRepo::Local(git_repository) => {1155 } => {
1240 cx.background_spawn(1156 client
1241 async move { git_repository.get_remotes(branch_name.as_deref()) },1157 .request(proto::SetIndexText {
1242 )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 }
1243 }1167 }
1244 GitRepo::Remote {1168 })
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 }
1268 }1169 }
1269 1170
1270 fn send_message(&self, message: Message) -> oneshot::Receiver<anyhow::Result<()>> {1171 pub fn get_remotes(
1271 let (result_tx, result_rx) = futures::channel::oneshot::channel();1172 &self,
1272 self.update_sender.unbounded_send((message, result_tx)).ok();1173 branch_name: Option<String>,
1273 result_rx1174 ) -> 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 })
1274 }1207 }
1275}1208}