pub mod git_traversal; use crate::{ buffer_store::{BufferStore, BufferStoreEvent}, worktree_store::{WorktreeStore, WorktreeStoreEvent}, ProjectEnvironment, ProjectItem, ProjectPath,};use anyhow::{anyhow, bail, Context as _, Result};use askpass::{AskPassDelegate, AskPassSession};use buffer_diff::{BufferDiff, BufferDiffEvent};use client::ProjectId;use collections::HashMap;use fs::Fs;use futures::{ channel::{mpsc, oneshot}, future::{self, OptionFuture, Shared}, FutureExt as _, StreamExt as _,};use git::{ blame::Blame, parse_git_remote_url, repository::{ Branch, CommitDetails, DiffType, GitIndex, GitRepository, GitRepositoryCheckpoint, PushOptions, Remote, RemoteCommandOutput, RepoPath, ResetMode, }, status::{FileStatus, GitStatus}, BuildPermalinkParams, GitHostingProviderRegistry,};use gpui::{ App, AppContext, AsyncApp, Context, Entity, EventEmitter, SharedString, Subscription, Task, WeakEntity,};use language::{ proto::{deserialize_version, serialize_version}, Buffer, BufferEvent, Language, LanguageRegistry,};use parking_lot::Mutex;use rpc::{ proto::{self, git_reset, FromProto, ToProto, SSH_PROJECT_ID}, AnyProtoClient, TypedEnvelope,};use serde::Deserialize;use settings::WorktreeId;use std::{ collections::{hash_map, VecDeque}, future::Future, ops::Range, path::{Path, PathBuf}, sync::Arc,};use sum_tree::TreeSet;use text::BufferId;use util::{debug_panic, maybe, ResultExt};use worktree::{ proto_to_branch, File, PathKey, ProjectEntryId, RepositoryEntry, StatusEntry, UpdatedGitRepositoriesSet, Worktree,}; pub struct GitStore { state: GitStoreState, buffer_store: Entity<BufferStore>, worktree_store: Entity<WorktreeStore>, repositories: HashMap<ProjectEntryId, Entity<Repository>>, active_repo_id: Option<ProjectEntryId>, #[allow(clippy::type_complexity)] loading_diffs: HashMap<(BufferId, DiffKind), Shared<Task<Result<Entity<BufferDiff>, Arc<anyhow::Error>>>>>, diffs: HashMap<BufferId, Entity<BufferDiffState>>, update_sender: mpsc::UnboundedSender<GitJob>, shared_diffs: HashMap<proto::PeerId, HashMap<BufferId, SharedDiffs>>, _subscriptions: [Subscription; 2],} #[derive(Default)]struct SharedDiffs { unstaged: Option<Entity<BufferDiff>>, uncommitted: Option<Entity<BufferDiff>>,} #[derive(Default)]struct BufferDiffState { unstaged_diff: Option<WeakEntity<BufferDiff>>, uncommitted_diff: Option<WeakEntity<BufferDiff>>, recalculate_diff_task: Option<Task<Result<()>>>, language: Option<Arc<Language>>, language_registry: Option<Arc<LanguageRegistry>>, diff_updated_futures: Vec<oneshot::Sender<()>>, hunk_staging_operation_count: usize, head_text: Option<Arc<String>>, index_text: Option<Arc<String>>, head_changed: bool, index_changed: bool, language_changed: bool,} #[derive(Clone, Debug)]enum DiffBasesChange { SetIndex(Option<String>), SetHead(Option<String>), SetEach { index: Option<String>, head: Option<String>, }, SetBoth(Option<String>),} #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]enum DiffKind { Unstaged, Uncommitted,} enum GitStoreState { Local { downstream_client: Option<LocalDownstreamState>, environment: Entity<ProjectEnvironment>, fs: Arc<dyn Fs>, }, Ssh { upstream_client: AnyProtoClient, upstream_project_id: ProjectId, downstream_client: Option<(AnyProtoClient, ProjectId)>, environment: Entity<ProjectEnvironment>, }, Remote { upstream_client: AnyProtoClient, project_id: ProjectId, },} enum DownstreamUpdate { UpdateRepository(RepositoryEntry), RemoveRepository(ProjectEntryId),} struct LocalDownstreamState { client: AnyProtoClient, project_id: ProjectId, updates_tx: mpsc::UnboundedSender<DownstreamUpdate>, _task: Task<Result<()>>,} #[derive(Clone)]pub struct GitStoreCheckpoint { checkpoints_by_work_dir_abs_path: HashMap<PathBuf, GitRepositoryCheckpoint>,} #[derive(Clone, Debug)]pub struct GitStoreDiff { diffs_by_work_dir_abs_path: HashMap<PathBuf, String>,} #[derive(Clone, Debug)]pub struct GitStoreIndex { indices_by_work_dir_abs_path: HashMap<PathBuf, GitIndex>,} #[derive(Default)]pub struct GitStoreStatus { #[allow(dead_code)] statuses_by_work_dir_abs_path: HashMap<PathBuf, GitStatus>,} pub struct Repository { pub repository_entry: RepositoryEntry, pub merge_message: Option<String>, pub completed_scan_id: usize, commit_message_buffer: Option<Entity<Buffer>>, git_store: WeakEntity<GitStore>, project_environment: Option<WeakEntity<ProjectEnvironment>>, pub worktree_id: Option<WorktreeId>, state: RepositoryState, job_sender: mpsc::UnboundedSender<GitJob>, askpass_delegates: Arc<Mutex<HashMap<u64, AskPassDelegate>>>, latest_askpass_id: u64,} #[derive(Clone)]enum RepositoryState { Local(Arc<dyn GitRepository>), Remote { project_id: ProjectId, client: AnyProtoClient, work_directory_id: ProjectEntryId, },} #[derive(Debug)]pub enum GitEvent { ActiveRepositoryChanged, FileSystemUpdated, GitStateUpdated, IndexWriteError(anyhow::Error),} struct GitJob { job: Box<dyn FnOnce(&mut AsyncApp) -> Task<()>>, key: Option<GitJobKey>,} #[derive(PartialEq, Eq)]enum GitJobKey { WriteIndex(RepoPath), BatchReadIndex(ProjectEntryId),} impl EventEmitter<GitEvent> for GitStore {} impl GitStore { pub fn local( worktree_store: &Entity<WorktreeStore>, buffer_store: Entity<BufferStore>, environment: Entity<ProjectEnvironment>, fs: Arc<dyn Fs>, cx: &mut Context<Self>, ) -> Self { Self::new( worktree_store.clone(), buffer_store, GitStoreState::Local { downstream_client: None, environment, fs, }, cx, ) } pub fn remote( worktree_store: &Entity<WorktreeStore>, buffer_store: Entity<BufferStore>, upstream_client: AnyProtoClient, project_id: ProjectId, cx: &mut Context<Self>, ) -> Self { Self::new( worktree_store.clone(), buffer_store, GitStoreState::Remote { upstream_client, project_id, }, cx, ) } pub fn ssh( worktree_store: &Entity<WorktreeStore>, buffer_store: Entity<BufferStore>, environment: Entity<ProjectEnvironment>, upstream_client: AnyProtoClient, cx: &mut Context<Self>, ) -> Self { Self::new( worktree_store.clone(), buffer_store, GitStoreState::Ssh { upstream_client, upstream_project_id: ProjectId(SSH_PROJECT_ID), downstream_client: None, environment, }, cx, ) } fn new( worktree_store: Entity<WorktreeStore>, buffer_store: Entity<BufferStore>, state: GitStoreState, cx: &mut Context<Self>, ) -> Self { let update_sender = Self::spawn_git_worker(cx); let _subscriptions = [ cx.subscribe(&worktree_store, Self::on_worktree_store_event), cx.subscribe(&buffer_store, Self::on_buffer_store_event), ]; GitStore { state, buffer_store, worktree_store, repositories: HashMap::default(), active_repo_id: None, update_sender, _subscriptions, loading_diffs: HashMap::default(), shared_diffs: HashMap::default(), diffs: HashMap::default(), } } pub fn init(client: &AnyProtoClient) { client.add_entity_request_handler(Self::handle_get_remotes); client.add_entity_request_handler(Self::handle_get_branches); 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_git_init); client.add_entity_request_handler(Self::handle_push); client.add_entity_request_handler(Self::handle_pull); client.add_entity_request_handler(Self::handle_fetch); client.add_entity_request_handler(Self::handle_stage); client.add_entity_request_handler(Self::handle_unstage); client.add_entity_request_handler(Self::handle_commit); client.add_entity_request_handler(Self::handle_reset); client.add_entity_request_handler(Self::handle_show); client.add_entity_request_handler(Self::handle_checkout_files); client.add_entity_request_handler(Self::handle_open_commit_message_buffer); client.add_entity_request_handler(Self::handle_set_index_text); client.add_entity_request_handler(Self::handle_askpass); client.add_entity_request_handler(Self::handle_check_for_pushed_commits); client.add_entity_request_handler(Self::handle_git_diff); client.add_entity_request_handler(Self::handle_open_unstaged_diff); client.add_entity_request_handler(Self::handle_open_uncommitted_diff); client.add_entity_message_handler(Self::handle_update_diff_bases); client.add_entity_request_handler(Self::handle_get_permalink_to_line); client.add_entity_request_handler(Self::handle_blame_buffer); client.add_entity_message_handler(Self::handle_update_repository); client.add_entity_message_handler(Self::handle_remove_repository); } pub fn is_local(&self) -> bool { matches!(self.state, GitStoreState::Local { .. }) } pub fn shared(&mut self, project_id: u64, client: AnyProtoClient, cx: &mut Context<Self>) { match &mut self.state { GitStoreState::Ssh { downstream_client, .. } => { for repo in self.repositories.values() { client .send(repo.read(cx).repository_entry.initial_update(project_id)) .log_err(); } *downstream_client = Some((client, ProjectId(project_id))); } GitStoreState::Local { downstream_client, .. } => { let mut snapshots = HashMap::default(); let (updates_tx, mut updates_rx) = mpsc::unbounded(); for repo in self.repositories.values() { updates_tx .unbounded_send(DownstreamUpdate::UpdateRepository( repo.read(cx).repository_entry.clone(), )) .ok(); } *downstream_client = Some(LocalDownstreamState { client: client.clone(), project_id: ProjectId(project_id), updates_tx, _task: cx.spawn(async move |this, cx| { cx.background_spawn(async move { while let Some(update) = updates_rx.next().await { match update { DownstreamUpdate::UpdateRepository(snapshot) => { if let Some(old_snapshot) = snapshots.get_mut(&snapshot.work_directory_id) { let update = snapshot.build_update(old_snapshot, project_id); *old_snapshot = snapshot; client.send(update)?; } else { let update = snapshot.initial_update(project_id); client.send(update)?; snapshots.insert(snapshot.work_directory_id, snapshot); } } DownstreamUpdate::RemoveRepository(id) => { client.send(proto::RemoveRepository { project_id, id: id.to_proto(), })?; } } } anyhow::Ok(()) }) .await .ok(); this.update(cx, |this, _| { if let GitStoreState::Local { downstream_client, .. } = &mut this.state { downstream_client.take(); } else { unreachable!("unshared called on remote store"); } }) }), }); } GitStoreState::Remote { .. } => { debug_panic!("shared called on remote store"); } } } pub fn unshared(&mut self, _cx: &mut Context<Self>) { match &mut self.state { GitStoreState::Local { downstream_client, .. } => { downstream_client.take(); } GitStoreState::Ssh { downstream_client, .. } => { downstream_client.take(); } GitStoreState::Remote { .. } => { debug_panic!("unshared called on remote store"); } } self.shared_diffs.clear(); } pub(crate) fn forget_shared_diffs_for(&mut self, peer_id: &proto::PeerId) { self.shared_diffs.remove(peer_id); } pub fn active_repository(&self) -> Option<Entity<Repository>> { self.active_repo_id .as_ref() .map(|id| self.repositories[&id].clone()) } pub fn open_unstaged_diff( &mut self, buffer: Entity<Buffer>, cx: &mut Context<Self>, ) -> Task<Result<Entity<BufferDiff>>> { let buffer_id = buffer.read(cx).remote_id(); if let Some(diff_state) = self.diffs.get(&buffer_id) { if let Some(unstaged_diff) = diff_state .read(cx) .unstaged_diff .as_ref() .and_then(|weak| weak.upgrade()) { if let Some(task) = diff_state.update(cx, |diff_state, _| diff_state.wait_for_recalculation()) { return cx.background_executor().spawn(async move { task.await?; Ok(unstaged_diff) }); } return Task::ready(Ok(unstaged_diff)); } } let task = match self.loading_diffs.entry((buffer_id, DiffKind::Unstaged)) { hash_map::Entry::Occupied(e) => e.get().clone(), hash_map::Entry::Vacant(entry) => { let staged_text = self.state.load_staged_text(&buffer, &self.buffer_store, cx); entry .insert( cx.spawn(async move |this, cx| { Self::open_diff_internal( this, DiffKind::Unstaged, staged_text.await.map(DiffBasesChange::SetIndex), buffer, cx, ) .await .map_err(Arc::new) }) .shared(), ) .clone() } }; cx.background_spawn(async move { task.await.map_err(|e| anyhow!("{e}")) }) } pub fn open_uncommitted_diff( &mut self, buffer: Entity<Buffer>, cx: &mut Context<Self>, ) -> Task<Result<Entity<BufferDiff>>> { let buffer_id = buffer.read(cx).remote_id(); if let Some(diff_state) = self.diffs.get(&buffer_id) { if let Some(uncommitted_diff) = diff_state .read(cx) .uncommitted_diff .as_ref() .and_then(|weak| weak.upgrade()) { if let Some(task) = diff_state.update(cx, |diff_state, _| diff_state.wait_for_recalculation()) {Lines 1–500 of 3938. Load more
pub mod git_traversal; use crate::{ buffer_store::{BufferStore, BufferStoreEvent}, worktree_store::{WorktreeStore, WorktreeStoreEvent}, ProjectEnvironment, ProjectItem, ProjectPath, }; use anyhow::{anyhow, bail, Context as _, Result}; use askpass::{AskPassDelegate, AskPassSession}; use buffer_diff::{BufferDiff, BufferDiffEvent}; use client::ProjectId; use collections::HashMap; use fs::Fs; use futures::{ channel::{mpsc, oneshot}, future::{self, OptionFuture, Shared}, FutureExt as _, StreamExt as _, }; use git::{ blame::Blame, parse_git_remote_url, repository::{ Branch, CommitDetails, DiffType, GitIndex, GitRepository, GitRepositoryCheckpoint, PushOptions, Remote, RemoteCommandOutput, RepoPath, ResetMode, }, status::{FileStatus, GitStatus}, BuildPermalinkParams, GitHostingProviderRegistry, }; use gpui::{ App, AppContext, AsyncApp, Context, Entity, EventEmitter, SharedString, Subscription, Task, WeakEntity, }; use language::{ proto::{deserialize_version, serialize_version}, Buffer, BufferEvent, Language, LanguageRegistry, }; use parking_lot::Mutex; use rpc::{ proto::{self, git_reset, FromProto, ToProto, SSH_PROJECT_ID}, AnyProtoClient, TypedEnvelope, }; use serde::Deserialize; use settings::WorktreeId; use std::{ collections::{hash_map, VecDeque}, future::Future, ops::Range, path::{Path, PathBuf}, sync::Arc, }; use sum_tree::TreeSet; use text::BufferId; use util::{debug_panic, maybe, ResultExt}; use worktree::{ proto_to_branch, File, PathKey, ProjectEntryId, RepositoryEntry, StatusEntry, UpdatedGitRepositoriesSet, Worktree, }; pub struct GitStore { state: GitStoreState, buffer_store: Entity<BufferStore>, worktree_store: Entity<WorktreeStore>, repositories: HashMap<ProjectEntryId, Entity<Repository>>, active_repo_id: Option<ProjectEntryId>, #[allow(clippy::type_complexity)] loading_diffs: HashMap<(BufferId, DiffKind), Shared<Task<Result<Entity<BufferDiff>, Arc<anyhow::Error>>>>>, diffs: HashMap<BufferId, Entity<BufferDiffState>>, update_sender: mpsc::UnboundedSender<GitJob>, shared_diffs: HashMap<proto::PeerId, HashMap<BufferId, SharedDiffs>>, _subscriptions: [Subscription; 2], } #[derive(Default)] struct SharedDiffs { unstaged: Option<Entity<BufferDiff>>, uncommitted: Option<Entity<BufferDiff>>, } #[derive(Default)] struct BufferDiffState { unstaged_diff: Option<WeakEntity<BufferDiff>>, uncommitted_diff: Option<WeakEntity<BufferDiff>>, recalculate_diff_task: Option<Task<Result<()>>>, language: Option<Arc<Language>>, language_registry: Option<Arc<LanguageRegistry>>, diff_updated_futures: Vec<oneshot::Sender<()>>, hunk_staging_operation_count: usize, head_text: Option<Arc<String>>, index_text: Option<Arc<String>>, head_changed: bool, index_changed: bool, language_changed: bool, } #[derive(Clone, Debug)] enum DiffBasesChange { SetIndex(Option<String>), SetHead(Option<String>), SetEach { index: Option<String>, head: Option<String>, }, SetBoth(Option<String>), } #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] enum DiffKind { Unstaged, Uncommitted, } enum GitStoreState { Local { downstream_client: Option<LocalDownstreamState>, environment: Entity<ProjectEnvironment>, fs: Arc<dyn Fs>, }, Ssh { upstream_client: AnyProtoClient, upstream_project_id: ProjectId, downstream_client: Option<(AnyProtoClient, ProjectId)>, environment: Entity<ProjectEnvironment>, }, Remote { upstream_client: AnyProtoClient, project_id: ProjectId, }, } enum DownstreamUpdate { UpdateRepository(RepositoryEntry), RemoveRepository(ProjectEntryId), } struct LocalDownstreamState { client: AnyProtoClient, project_id: ProjectId, updates_tx: mpsc::UnboundedSender<DownstreamUpdate>, _task: Task<Result<()>>, } #[derive(Clone)] pub struct GitStoreCheckpoint { checkpoints_by_work_dir_abs_path: HashMap<PathBuf, GitRepositoryCheckpoint>, } #[derive(Clone, Debug)] pub struct GitStoreDiff { diffs_by_work_dir_abs_path: HashMap<PathBuf, String>, } #[derive(Clone, Debug)] pub struct GitStoreIndex { indices_by_work_dir_abs_path: HashMap<PathBuf, GitIndex>, } #[derive(Default)] pub struct GitStoreStatus { #[allow(dead_code)] statuses_by_work_dir_abs_path: HashMap<PathBuf, GitStatus>, } pub struct Repository { pub repository_entry: RepositoryEntry, pub merge_message: Option<String>, pub completed_scan_id: usize, commit_message_buffer: Option<Entity<Buffer>>, git_store: WeakEntity<GitStore>, project_environment: Option<WeakEntity<ProjectEnvironment>>, pub worktree_id: Option<WorktreeId>, state: RepositoryState, job_sender: mpsc::UnboundedSender<GitJob>, askpass_delegates: Arc<Mutex<HashMap<u64, AskPassDelegate>>>, latest_askpass_id: u64, } #[derive(Clone)] enum RepositoryState { Local(Arc<dyn GitRepository>), Remote { project_id: ProjectId, client: AnyProtoClient, work_directory_id: ProjectEntryId, }, } #[derive(Debug)] pub enum GitEvent { ActiveRepositoryChanged, FileSystemUpdated, GitStateUpdated, IndexWriteError(anyhow::Error), } struct GitJob { job: Box<dyn FnOnce(&mut AsyncApp) -> Task<()>>, key: Option<GitJobKey>, } #[derive(PartialEq, Eq)] enum GitJobKey { WriteIndex(RepoPath), BatchReadIndex(ProjectEntryId), } impl EventEmitter<GitEvent> for GitStore {} impl GitStore { pub fn local( worktree_store: &Entity<WorktreeStore>, buffer_store: Entity<BufferStore>, environment: Entity<ProjectEnvironment>, fs: Arc<dyn Fs>, cx: &mut Context<Self>, ) -> Self { Self::new( worktree_store.clone(), buffer_store, GitStoreState::Local { downstream_client: None, environment, fs, }, cx, ) } pub fn remote( worktree_store: &Entity<WorktreeStore>, buffer_store: Entity<BufferStore>, upstream_client: AnyProtoClient, project_id: ProjectId, cx: &mut Context<Self>, ) -> Self { Self::new( worktree_store.clone(), buffer_store, GitStoreState::Remote { upstream_client, project_id, }, cx, ) } pub fn ssh( worktree_store: &Entity<WorktreeStore>, buffer_store: Entity<BufferStore>, environment: Entity<ProjectEnvironment>, upstream_client: AnyProtoClient, cx: &mut Context<Self>, ) -> Self { Self::new( worktree_store.clone(), buffer_store, GitStoreState::Ssh { upstream_client, upstream_project_id: ProjectId(SSH_PROJECT_ID), downstream_client: None, environment, }, cx, ) } fn new( worktree_store: Entity<WorktreeStore>, buffer_store: Entity<BufferStore>, state: GitStoreState, cx: &mut Context<Self>, ) -> Self { let update_sender = Self::spawn_git_worker(cx); let _subscriptions = [ cx.subscribe(&worktree_store, Self::on_worktree_store_event), cx.subscribe(&buffer_store, Self::on_buffer_store_event), ]; GitStore { state, buffer_store, worktree_store, repositories: HashMap::default(), active_repo_id: None, update_sender, _subscriptions, loading_diffs: HashMap::default(), shared_diffs: HashMap::default(), diffs: HashMap::default(), } } pub fn init(client: &AnyProtoClient) { client.add_entity_request_handler(Self::handle_get_remotes); client.add_entity_request_handler(Self::handle_get_branches); 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_git_init); client.add_entity_request_handler(Self::handle_push); client.add_entity_request_handler(Self::handle_pull); client.add_entity_request_handler(Self::handle_fetch); client.add_entity_request_handler(Self::handle_stage); client.add_entity_request_handler(Self::handle_unstage); client.add_entity_request_handler(Self::handle_commit); client.add_entity_request_handler(Self::handle_reset); client.add_entity_request_handler(Self::handle_show); client.add_entity_request_handler(Self::handle_checkout_files); client.add_entity_request_handler(Self::handle_open_commit_message_buffer); client.add_entity_request_handler(Self::handle_set_index_text); client.add_entity_request_handler(Self::handle_askpass); client.add_entity_request_handler(Self::handle_check_for_pushed_commits); client.add_entity_request_handler(Self::handle_git_diff); client.add_entity_request_handler(Self::handle_open_unstaged_diff); client.add_entity_request_handler(Self::handle_open_uncommitted_diff); client.add_entity_message_handler(Self::handle_update_diff_bases); client.add_entity_request_handler(Self::handle_get_permalink_to_line); client.add_entity_request_handler(Self::handle_blame_buffer); client.add_entity_message_handler(Self::handle_update_repository); client.add_entity_message_handler(Self::handle_remove_repository); } pub fn is_local(&self) -> bool { matches!(self.state, GitStoreState::Local { .. }) } pub fn shared(&mut self, project_id: u64, client: AnyProtoClient, cx: &mut Context<Self>) { match &mut self.state { GitStoreState::Ssh { downstream_client, .. } => { for repo in self.repositories.values() { client .send(repo.read(cx).repository_entry.initial_update(project_id)) .log_err(); } *downstream_client = Some((client, ProjectId(project_id))); } GitStoreState::Local { downstream_client, .. } => { let mut snapshots = HashMap::default(); let (updates_tx, mut updates_rx) = mpsc::unbounded(); for repo in self.repositories.values() { updates_tx .unbounded_send(DownstreamUpdate::UpdateRepository( repo.read(cx).repository_entry.clone(), )) .ok(); } *downstream_client = Some(LocalDownstreamState { client: client.clone(), project_id: ProjectId(project_id), updates_tx, _task: cx.spawn(async move |this, cx| { cx.background_spawn(async move { while let Some(update) = updates_rx.next().await { match update { DownstreamUpdate::UpdateRepository(snapshot) => { if let Some(old_snapshot) = snapshots.get_mut(&snapshot.work_directory_id) { let update = snapshot.build_update(old_snapshot, project_id); *old_snapshot = snapshot; client.send(update)?; } else { let update = snapshot.initial_update(project_id); client.send(update)?; snapshots.insert(snapshot.work_directory_id, snapshot); } } DownstreamUpdate::RemoveRepository(id) => { client.send(proto::RemoveRepository { project_id, id: id.to_proto(), })?; } } } anyhow::Ok(()) }) .await .ok(); this.update(cx, |this, _| { if let GitStoreState::Local { downstream_client, .. } = &mut this.state { downstream_client.take(); } else { unreachable!("unshared called on remote store"); } }) }), }); } GitStoreState::Remote { .. } => { debug_panic!("shared called on remote store"); } } } pub fn unshared(&mut self, _cx: &mut Context<Self>) { match &mut self.state { GitStoreState::Local { downstream_client, .. } => { downstream_client.take(); } GitStoreState::Ssh { downstream_client, .. } => { downstream_client.take(); } GitStoreState::Remote { .. } => { debug_panic!("unshared called on remote store"); } } self.shared_diffs.clear(); } pub(crate) fn forget_shared_diffs_for(&mut self, peer_id: &proto::PeerId) { self.shared_diffs.remove(peer_id); } pub fn active_repository(&self) -> Option<Entity<Repository>> { self.active_repo_id .as_ref() .map(|id| self.repositories[&id].clone()) } pub fn open_unstaged_diff( &mut self, buffer: Entity<Buffer>, cx: &mut Context<Self>, ) -> Task<Result<Entity<BufferDiff>>> { let buffer_id = buffer.read(cx).remote_id(); if let Some(diff_state) = self.diffs.get(&buffer_id) { if let Some(unstaged_diff) = diff_state .read(cx) .unstaged_diff .as_ref() .and_then(|weak| weak.upgrade()) { if let Some(task) = diff_state.update(cx, |diff_state, _| diff_state.wait_for_recalculation()) { return cx.background_executor().spawn(async move { task.await?; Ok(unstaged_diff) }); } return Task::ready(Ok(unstaged_diff)); } } let task = match self.loading_diffs.entry((buffer_id, DiffKind::Unstaged)) { hash_map::Entry::Occupied(e) => e.get().clone(), hash_map::Entry::Vacant(entry) => { let staged_text = self.state.load_staged_text(&buffer, &self.buffer_store, cx); entry .insert( cx.spawn(async move |this, cx| { Self::open_diff_internal( this, DiffKind::Unstaged, staged_text.await.map(DiffBasesChange::SetIndex), buffer, cx, ) .await .map_err(Arc::new) }) .shared(), ) .clone() } }; cx.background_spawn(async move { task.await.map_err(|e| anyhow!("{e}")) }) } pub fn open_uncommitted_diff( &mut self, buffer: Entity<Buffer>, cx: &mut Context<Self>, ) -> Task<Result<Entity<BufferDiff>>> { let buffer_id = buffer.read(cx).remote_id(); if let Some(diff_state) = self.diffs.get(&buffer_id) { if let Some(uncommitted_diff) = diff_state .read(cx) .uncommitted_diff .as_ref() .and_then(|weak| weak.upgrade()) { if let Some(task) = diff_state.update(cx, |diff_state, _| diff_state.wait_for_recalculation()) {