diff --git a/crates/project/src/git_store.rs b/crates/project/src/git_store.rs index e29710682b45125ff06a0cc8390e768a11289c6d..3634964fa6ad55e53f9593b2eb001bd5ed6f00a3 100644 --- a/crates/project/src/git_store.rs +++ b/crates/project/src/git_store.rs @@ -31,8 +31,8 @@ use git::{ }, stash::{GitStash, StashEntry}, status::{ - DiffTreeType, FileStatus, GitSummary, StatusCode, TrackedStatus, TreeDiff, TreeDiffStatus, - UnmergedStatus, UnmergedStatusCode, + DiffTreeType, FileStatus, GitSummary, StageStatus, StatusCode, TrackedStatus, TreeDiff, + TreeDiffStatus, UnmergedStatus, UnmergedStatusCode, }, }; use gpui::{ @@ -244,10 +244,90 @@ pub struct MergeDetails { pub heads: Vec>, } +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum PendingOperationStatus { + Staged, + Unstaged, + Reverted, + Unchanged, +} + +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct PendingOperations { + repo_path: RepoPath, + // TODO: move this into StatusEntry + staging: StageStatus, + ops: Vec, +} + +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct PendingOperation { + id: usize, + finished: bool, + status: PendingOperationStatus, +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct PendingOperationsSummary { + finished: bool, + status: PendingOperationStatus, + count: usize, +} + +impl Default for PendingOperationsSummary { + fn default() -> Self { + Self { + finished: false, + status: PendingOperationStatus::Unstaged, + count: 0, + } + } +} + +impl sum_tree::ContextLessSummary for PendingOperationsSummary { + fn zero() -> Self { + Default::default() + } + + fn add_summary(&mut self, rhs: &Self) { + self.finished = self.finished || rhs.finished; + self.count += rhs.count; + self.status = rhs.status; + } +} + +impl sum_tree::Item for PendingOperations { + type Summary = PathSummary; + + fn summary(&self, _: ::Context<'_>) -> Self::Summary { + let mut item_summary = PendingOperationsSummary { + count: self.ops.len(), + ..Default::default() + }; + if let Some(op) = self.ops.last() { + item_summary.finished = op.finished; + item_summary.status = op.status; + } + PathSummary { + max_path: self.repo_path.0.clone(), + item_summary, + } + } +} + +impl sum_tree::KeyedItem for PendingOperations { + type Key = PathKey; + + fn key(&self) -> Self::Key { + PathKey(self.repo_path.0.clone()) + } +} + #[derive(Clone, Debug, PartialEq, Eq)] pub struct RepositorySnapshot { pub id: RepositoryId, pub statuses_by_path: SumTree, + pub pending_ops_by_path: SumTree, pub work_directory_abs_path: Arc, pub path_style: PathStyle, pub branch: Option, @@ -2903,6 +2983,7 @@ impl RepositorySnapshot { Self { id, statuses_by_path: Default::default(), + pending_ops_by_path: Default::default(), work_directory_abs_path, branch: None, head_commit: None, @@ -3030,6 +3111,21 @@ impl RepositorySnapshot { .cloned() } + pub fn pending_ops(&self) -> impl Iterator + '_ { + self.pending_ops_by_path.iter().cloned() + } + + pub fn pending_ops_summary(&self) -> PendingOperationsSummary { + self.pending_ops_by_path.summary().item_summary + } + + pub fn pending_ops_for_path(&self, path: &RepoPath) -> Vec { + self.pending_ops_by_path + .get(&PathKey(path.0.clone()), ()) + .map(|ops| ops.ops.clone()) + .unwrap_or(Vec::new()) + } + pub fn abs_path_to_repo_path(&self, abs_path: &Path) -> Option { Self::abs_path_to_repo_path_inner(&self.work_directory_abs_path, abs_path, self.path_style) } @@ -5331,6 +5427,8 @@ async fn compute_snapshot( MergeDetails::load(&backend, &statuses_by_path, &prev_snapshot).await?; log::debug!("new merge details (changed={merge_heads_changed:?}): {merge_details:?}"); + let pending_ops_by_path = prev_snapshot.pending_ops_by_path.clone(); + if merge_heads_changed { events.push(RepositoryEvent::MergeHeadsChanged); } @@ -5356,6 +5454,7 @@ async fn compute_snapshot( let snapshot = RepositorySnapshot { id, statuses_by_path, + pending_ops_by_path, work_directory_abs_path, path_style: prev_snapshot.path_style, scan_id: prev_snapshot.scan_id + 1,