From ab6a19c839663e113874b3fa162cd01d88f39083 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Mon, 3 Nov 2025 12:14:44 +0100 Subject: [PATCH] git: Flatten the collection and index by ID --- crates/git_ui/src/git_panel.rs | 8 +- crates/project/src/git_store.rs | 118 +++++++++++---------- crates/project/src/git_store/pending_op.rs | 74 ++++++------- 3 files changed, 100 insertions(+), 100 deletions(-) diff --git a/crates/git_ui/src/git_panel.rs b/crates/git_ui/src/git_panel.rs index 3d4a2ff45ff9cb936c038b77bb6fee0d9b81bdff..73ef8212bdc1004679b395861ca8827c2327e4b2 100644 --- a/crates/git_ui/src/git_panel.rs +++ b/crates/git_ui/src/git_panel.rs @@ -1304,7 +1304,13 @@ impl GitPanel { }); let repository = active_repository.read(cx); - dbg!(&repository.snapshot().pending_ops_by_path); + for entry in &entries { + println!( + "{:?} -> {:?}", + &entry.repo_path, + repository.snapshot().pending_ops_for_path(&entry.repo_path) + ); + } self.update_counts(repository); cx.notify(); diff --git a/crates/project/src/git_store.rs b/crates/project/src/git_store.rs index 8c7273621bbfca0caaffae8ce1cc6603ad75f552..46740064c8158089a207230a6a7b36486d0b0cdb 100644 --- a/crates/project/src/git_store.rs +++ b/crates/project/src/git_store.rs @@ -45,7 +45,7 @@ use language::{ proto::{deserialize_version, serialize_version}, }; use parking_lot::Mutex; -use pending_op::{PendingOp, PendingOps, Status as PendingOpStatus}; +use pending_op::{PendingOp, PendingOpId, PendingOpStatus}; use postage::stream::Stream as _; use rpc::{ AnyProtoClient, TypedEnvelope, @@ -250,7 +250,7 @@ pub struct MergeDetails { pub struct RepositorySnapshot { pub id: RepositoryId, pub statuses_by_path: SumTree, - pub pending_ops_by_path: SumTree, + pub pending_ops: SumTree, pub work_directory_abs_path: Arc, pub path_style: PathStyle, pub branch: Option, @@ -2906,7 +2906,7 @@ impl RepositorySnapshot { Self { id, statuses_by_path: Default::default(), - pending_ops_by_path: Default::default(), + pending_ops: Default::default(), work_directory_abs_path, branch: None, head_commit: None, @@ -3034,11 +3034,16 @@ impl RepositorySnapshot { .cloned() } - pub fn pending_ops_for_path(&self, path: &RepoPath) -> Option> { - self.pending_ops_by_path - .get(&PathKey(path.0.clone()), ()) - .map(|ops| &ops.ops) - .cloned() + pub fn pending_op_by_id(&self, id: PendingOpId) -> Option { + self.pending_ops.get(&id, ()).cloned() + } + + pub fn pending_ops_for_path(&self, path: &RepoPath) -> Vec { + self.pending_ops + .filter::<_, PathKey>((), |sum| sum.max_path == path.0) + .into_iter() + .map(Clone::clone) + .collect() } pub fn abs_path_to_repo_path(&self, abs_path: &Path) -> Option { @@ -3776,10 +3781,20 @@ impl Repository { _ => None, }; - let entries_cloned = entries.clone(); + let mut ids = Vec::with_capacity(entries.len()); - for entry in &entries_cloned { - self.push_pending_op_for_path(entry, PendingOpStatus::Staged); + for entry in &entries { + let id = self.snapshot.pending_ops.summary().item_summary.max_id + 1; + self.snapshot.pending_ops.push( + PendingOp { + repo_path: entry.clone(), + id, + status: PendingOpStatus::Staged, + finished: false, + }, + (), + ); + ids.push(id); } cx.spawn(async move |this, cx| { @@ -3819,25 +3834,16 @@ 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, ()); - // } - // })?; + this.update(cx, |this, _| { + for id in ids { + if let Some(mut op) = this.snapshot.pending_op_by_id(id) { + op.finished = true; + this.snapshot + .pending_ops + .edit(vec![sum_tree::Edit::Insert(op)], ()); + } + } + })?; Ok(()) }) @@ -3864,8 +3870,20 @@ impl Repository { _ => None, }; + let mut ids = Vec::with_capacity(entries.len()); + for entry in &entries { - self.push_pending_op_for_path(entry, PendingOpStatus::Unstaged); + let id = self.snapshot.pending_ops.summary().item_summary.max_id + 1; + self.snapshot.pending_ops.push( + PendingOp { + repo_path: entry.clone(), + id, + status: PendingOpStatus::Unstaged, + finished: false, + }, + (), + ); + ids.push(id); } cx.spawn(async move |this, cx| { @@ -3905,6 +3923,17 @@ impl Repository { })? .await??; + this.update(cx, |this, _| { + for id in ids { + if let Some(mut op) = this.snapshot.pending_op_by_id(id) { + op.finished = true; + this.snapshot + .pending_ops + .edit(vec![sum_tree::Edit::Insert(op)], ()); + } + } + })?; + Ok(()) }) } @@ -5123,29 +5152,6 @@ impl Repository { pub fn barrier(&mut self) -> oneshot::Receiver<()> { self.send_job(None, |_, _| async {}) } - - fn push_pending_op_for_path(&mut self, path: &RepoPath, status: PendingOpStatus) { - let mut ops = Vec::new(); - let existing_ops = self.snapshot.pending_ops_for_path(path); - let id = existing_ops - .as_ref() - .map(|ops| ops.summary().max_id) - .unwrap_or(0) - + 1; - if let Some(existing_ops) = existing_ops { - ops.append(&mut existing_ops.items(())); - } - ops.push(PendingOp { - id, - status, - finished: false, - }); - let edit = sum_tree::Edit::Insert(PendingOps { - repo_path: path.clone(), - ops: SumTree::from_iter(ops, ()), - }); - self.snapshot.pending_ops_by_path.edit(vec![edit], ()); - } } fn get_permalink_in_rust_registry_src( @@ -5395,7 +5401,7 @@ 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(); + let pending_ops = prev_snapshot.pending_ops.clone(); if merge_heads_changed { events.push(RepositoryEvent::MergeHeadsChanged); @@ -5422,7 +5428,7 @@ async fn compute_snapshot( let snapshot = RepositorySnapshot { id, statuses_by_path, - pending_ops_by_path, + pending_ops, work_directory_abs_path, path_style: prev_snapshot.path_style, scan_id: prev_snapshot.scan_id + 1, diff --git a/crates/project/src/git_store/pending_op.rs b/crates/project/src/git_store/pending_op.rs index 776d7841e5730bd3895c4fc1afdea30d318fd98a..736d88ab4a86f5a12069eb5418a1533e1af466f9 100644 --- a/crates/project/src/git_store/pending_op.rs +++ b/crates/project/src/git_store/pending_op.rs @@ -1,9 +1,10 @@ use git::repository::RepoPath; -use sum_tree::{ContextLessSummary, Dimension, Item, KeyedItem, NoSummary, SumTree}; -use worktree::{PathKey, PathSummary}; +use std::ops::Add; +use sum_tree::{ContextLessSummary, Dimension, Item, KeyedItem}; +use worktree::PathSummary; #[derive(Clone, Copy, Debug, PartialEq, Eq)] -pub enum Status { +pub enum PendingOpStatus { Staged, Unstaged, Reverted, @@ -12,54 +13,33 @@ pub enum Status { #[derive(Clone, Debug, PartialEq, Eq)] pub struct PendingOp { - pub id: u16, - pub status: Status, + pub repo_path: RepoPath, + pub id: PendingOpId, + pub status: PendingOpStatus, pub finished: bool, } #[derive(Clone, Debug)] pub struct PendingOpSummary { - pub max_id: u16, - pub staged_count: u32, - pub unstaged_count: u32, -} - -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct PendingOps { - pub repo_path: RepoPath, - pub ops: SumTree, + pub max_id: PendingOpId, + pub staged_count: usize, + pub unstaged_count: usize, } #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] -pub struct PendingOpId(u16); +pub struct PendingOpId(pub usize); -impl Item for PendingOps { - type Summary = PathSummary; +impl Item for PendingOp { + 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, + item_summary: PendingOpSummary { + max_id: self.id, + staged_count: (self.status == PendingOpStatus::Staged) as usize, + unstaged_count: (self.status == PendingOpStatus::Unstaged) as usize, + }, } } } @@ -67,7 +47,7 @@ impl Item for PendingOp { impl ContextLessSummary for PendingOpSummary { fn zero() -> Self { Self { - max_id: 0, + max_id: PendingOpId(0), staged_count: 0, unstaged_count: 0, } @@ -84,16 +64,24 @@ impl KeyedItem for PendingOp { type Key = PendingOpId; fn key(&self) -> Self::Key { - PendingOpId(self.id) + self.id } } -impl Dimension<'_, PendingOpSummary> for PendingOpId { +impl Dimension<'_, PathSummary> for PendingOpId { fn zero(_cx: ()) -> Self { Self(0) } - fn add_summary(&mut self, summary: &PendingOpSummary, _cx: ()) { - self.0 = summary.max_id; + fn add_summary(&mut self, summary: &PathSummary, _cx: ()) { + *self = summary.item_summary.max_id; + } +} + +impl Add for PendingOpId { + type Output = PendingOpId; + + fn add(self, rhs: usize) -> Self::Output { + Self(self.0 + rhs) } }