diff --git a/crates/git_ui/src/git_panel.rs b/crates/git_ui/src/git_panel.rs index 0c43058c067aa9b6abcc333e58f7e4933d783b73..3d4a2ff45ff9cb936c038b77bb6fee0d9b81bdff 100644 --- a/crates/git_ui/src/git_panel.rs +++ b/crates/git_ui/src/git_panel.rs @@ -1302,7 +1302,9 @@ impl GitPanel { entries: entries.clone(), finished: false, }); + let repository = active_repository.read(cx); + dbg!(&repository.snapshot().pending_ops_by_path); self.update_counts(repository); cx.notify(); diff --git a/crates/project/src/git_store.rs b/crates/project/src/git_store.rs index 3634964fa6ad55e53f9593b2eb001bd5ed6f00a3..36231f2fd5d2157a5ff8d85abf199eb3fd05e469 100644 --- a/crates/project/src/git_store.rs +++ b/crates/project/src/git_store.rs @@ -1,6 +1,7 @@ pub mod branch_diff; mod conflict_set; pub mod git_traversal; +pub mod pending_op; use crate::{ ProjectEnvironment, ProjectItem, ProjectPath, @@ -31,8 +32,8 @@ use git::{ }, stash::{GitStash, StashEntry}, status::{ - DiffTreeType, FileStatus, GitSummary, StageStatus, StatusCode, TrackedStatus, TreeDiff, - TreeDiffStatus, UnmergedStatus, UnmergedStatusCode, + DiffTreeType, FileStatus, GitSummary, StatusCode, TrackedStatus, TreeDiff, TreeDiffStatus, + UnmergedStatus, UnmergedStatusCode, }, }; use gpui::{ @@ -44,6 +45,7 @@ use language::{ proto::{deserialize_version, serialize_version}, }; use parking_lot::Mutex; +use pending_op::{PendingOp, PendingOps, Status as PendingOpStatus}; use postage::stream::Stream as _; use rpc::{ AnyProtoClient, TypedEnvelope, @@ -244,90 +246,11 @@ 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 pending_ops_by_path: SumTree, pub work_directory_abs_path: Arc, pub path_style: PathStyle, pub branch: Option, @@ -3111,21 +3034,6 @@ 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) } @@ -3841,7 +3749,7 @@ impl Repository { } pub fn stage_entries( - &self, + &mut self, entries: Vec, cx: &mut Context, ) -> Task> { @@ -3861,6 +3769,31 @@ impl Repository { _ => None, }; + let entries_cloned = entries.clone(); + + for entry in &entries_cloned { + let mut ops = Vec::new(); + if let Some(mut existing) = self + .snapshot + .pending_ops_by_path + .get(&PathKey(entry.0.clone()), ()) + .map(|ops| ops.ops.items(())) + { + ops.append(&mut existing); + } + let id = ops.iter().map(|op| op.id).max().unwrap_or(0) + 1; + ops.push(PendingOp { + id, + status: PendingOpStatus::Staged, + finished: false, + }); + let edit = sum_tree::Edit::Insert(PendingOps { + repo_path: entry.clone(), + ops: SumTree::from_iter(ops, ()), + }); + self.snapshot.pending_ops_by_path.edit(vec![edit], ()); + } + cx.spawn(async move |this, cx| { for save_task in save_tasks { save_task.await?; @@ -3898,12 +3831,32 @@ impl Repository { })? .await??; + // this.update(cx, |this, _| { + // for entry in &entries_cloned { + // let key = PathKey(entry.0.clone()); + // let Some(pending) = this.snapshot.pending_ops_by_path.get(&key) else { continue; }; + // let Some(mut pending) = this.snapshot.pending_ops_by_path.remove(&key, ()) + // else { + // continue; + // }; + // for p in &mut pending.ops { + // if p.id == *op_id { + // p.finished = true; + // break; + // } + // } + // this.snapshot + // .pending_ops_by_path + // .insert_or_replace(pending, ()); + // } + // })?; + Ok(()) }) } pub fn unstage_entries( - &self, + &mut self, entries: Vec, cx: &mut Context, ) -> Task> { @@ -3964,7 +3917,7 @@ impl Repository { }) } - pub fn stage_all(&self, cx: &mut Context) -> Task> { + pub fn stage_all(&mut self, cx: &mut Context) -> Task> { let to_stage = self .cached_status() .filter(|entry| !entry.status.staging().is_fully_staged()) @@ -3973,7 +3926,7 @@ impl Repository { self.stage_entries(to_stage, cx) } - pub fn unstage_all(&self, cx: &mut Context) -> Task> { + pub fn unstage_all(&mut self, cx: &mut Context) -> Task> { let to_unstage = self .cached_status() .filter(|entry| entry.status.staging().has_staged()) diff --git a/crates/project/src/git_store/pending_op.rs b/crates/project/src/git_store/pending_op.rs new file mode 100644 index 0000000000000000000000000000000000000000..9f41f94a52f9f680a2e49704c902d934ff4f63c9 --- /dev/null +++ b/crates/project/src/git_store/pending_op.rs @@ -0,0 +1,99 @@ +use git::repository::RepoPath; +use sum_tree::{ContextLessSummary, Dimension, Item, KeyedItem, NoSummary, SumTree}; +use worktree::{PathKey, PathSummary}; + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum Status { + Staged, + Unstaged, + Reverted, + Unchanged, +} + +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct PendingOp { + pub id: u16, + pub status: Status, + pub finished: bool, +} + +#[derive(Clone, Debug)] +pub struct PendingOpSummary { + max_id: u16, + staged_count: u32, + unstaged_count: u32, +} + +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct PendingOps { + pub repo_path: RepoPath, + pub ops: SumTree, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] +pub struct PendingOpId(u16); + +impl Item for PendingOps { + type Summary = PathSummary; + + fn summary(&self, _cx: ()) -> Self::Summary { + PathSummary { + max_path: self.repo_path.0.clone(), + item_summary: NoSummary, + } + } +} + +impl KeyedItem for PendingOps { + type Key = PathKey; + + fn key(&self) -> Self::Key { + PathKey(self.repo_path.0.clone()) + } +} + +impl Item for PendingOp { + type Summary = PendingOpSummary; + + fn summary(&self, _cx: ()) -> Self::Summary { + PendingOpSummary { + max_id: self.id, + staged_count: (self.status == Status::Staged) as u32, + unstaged_count: (self.status == Status::Unstaged) as u32, + } + } +} + +impl ContextLessSummary for PendingOpSummary { + fn zero() -> Self { + Self { + max_id: 0, + staged_count: 0, + unstaged_count: 0, + } + } + + fn add_summary(&mut self, summary: &Self) { + self.max_id = summary.max_id; + self.staged_count += summary.staged_count; + self.unstaged_count += summary.unstaged_count; + } +} + +impl KeyedItem for PendingOp { + type Key = PendingOpId; + + fn key(&self) -> Self::Key { + PendingOpId(self.id) + } +} + +impl Dimension<'_, PendingOpSummary> for PendingOpId { + fn zero(_cx: ()) -> Self { + Self(0) + } + + fn add_summary(&mut self, summary: &PendingOpSummary, _cx: ()) { + self.0 = summary.max_id; + } +}