git.rs

  1use std::sync::Arc;
  2
  3use futures::channel::mpsc;
  4use futures::StreamExt as _;
  5use git::repository::{GitRepository, RepoPath};
  6use gpui::{AppContext, SharedString};
  7use settings::WorktreeId;
  8use util::ResultExt as _;
  9use worktree::RepositoryEntry;
 10
 11#[derive(Clone, Copy, Debug, PartialEq, Eq)]
 12pub enum StatusAction {
 13    Stage,
 14    Unstage,
 15}
 16
 17pub struct GitState {
 18    /// The current commit message being composed.
 19    pub commit_message: Option<SharedString>,
 20
 21    /// When a git repository is selected, this is used to track which repository's changes
 22    /// are currently being viewed or modified in the UI.
 23    pub active_repository: Option<(WorktreeId, RepositoryEntry, Arc<dyn GitRepository>)>,
 24
 25    pub update_sender: mpsc::UnboundedSender<(Arc<dyn GitRepository>, Vec<RepoPath>, StatusAction)>,
 26}
 27
 28impl GitState {
 29    pub fn new(cx: &AppContext) -> Self {
 30        let (tx, mut rx) =
 31            mpsc::unbounded::<(Arc<dyn GitRepository>, Vec<RepoPath>, StatusAction)>();
 32        cx.spawn(|cx| async move {
 33            while let Some((git_repo, paths, action)) = rx.next().await {
 34                cx.background_executor()
 35                    .spawn(async move {
 36                        match action {
 37                            StatusAction::Stage => git_repo.stage_paths(&paths),
 38                            StatusAction::Unstage => git_repo.unstage_paths(&paths),
 39                        }
 40                    })
 41                    .await
 42                    .log_err();
 43            }
 44        })
 45        .detach();
 46        GitState {
 47            commit_message: None,
 48            active_repository: None,
 49            update_sender: tx,
 50        }
 51    }
 52
 53    pub fn activate_repository(
 54        &mut self,
 55        worktree_id: WorktreeId,
 56        active_repository: RepositoryEntry,
 57        git_repo: Arc<dyn GitRepository>,
 58    ) {
 59        self.active_repository = Some((worktree_id, active_repository, git_repo));
 60    }
 61
 62    pub fn active_repository(
 63        &self,
 64    ) -> Option<&(WorktreeId, RepositoryEntry, Arc<dyn GitRepository>)> {
 65        self.active_repository.as_ref()
 66    }
 67
 68    pub fn commit_message(&mut self, message: Option<SharedString>) {
 69        self.commit_message = message;
 70    }
 71
 72    pub fn clear_commit_message(&mut self) {
 73        self.commit_message = None;
 74    }
 75
 76    pub fn stage_entry(&mut self, repo_path: RepoPath) {
 77        if let Some((_, _, git_repo)) = self.active_repository.as_ref() {
 78            let _ = self.update_sender.unbounded_send((
 79                git_repo.clone(),
 80                vec![repo_path],
 81                StatusAction::Stage,
 82            ));
 83        }
 84    }
 85
 86    pub fn unstage_entry(&mut self, repo_path: RepoPath) {
 87        if let Some((_, _, git_repo)) = self.active_repository.as_ref() {
 88            let _ = self.update_sender.unbounded_send((
 89                git_repo.clone(),
 90                vec![repo_path],
 91                StatusAction::Unstage,
 92            ));
 93        }
 94    }
 95
 96    pub fn stage_entries(&mut self, entries: Vec<RepoPath>) {
 97        if let Some((_, _, git_repo)) = self.active_repository.as_ref() {
 98            let _ =
 99                self.update_sender
100                    .unbounded_send((git_repo.clone(), entries, StatusAction::Stage));
101        }
102    }
103
104    fn act_on_all(&mut self, action: StatusAction) {
105        if let Some((_, active_repository, git_repo)) = self.active_repository.as_ref() {
106            let _ = self.update_sender.unbounded_send((
107                git_repo.clone(),
108                active_repository
109                    .status()
110                    .map(|entry| entry.repo_path)
111                    .collect(),
112                action,
113            ));
114        }
115    }
116
117    pub fn stage_all(&mut self) {
118        self.act_on_all(StatusAction::Stage);
119    }
120
121    pub fn unstage_all(&mut self) {
122        self.act_on_all(StatusAction::Unstage);
123    }
124}