From aba10b73d21f181bab1211313abe7b8a832d4ee2 Mon Sep 17 00:00:00 2001 From: "gcp-cherry-pick-bot[bot]" <98988430+gcp-cherry-pick-bot[bot]@users.noreply.github.com> Date: Tue, 4 Mar 2025 10:35:53 -0700 Subject: [PATCH] Git fix repo selection (cherry-pick #25996) (#25998) Cherry-picked Git fix repo selection (#25996) Release Notes: - git: Fixed a bug where staging/unstaging of hunks could use the wrong git repository if you had many open Co-authored-by: Conrad Irwin --- crates/project/src/buffer_store.rs | 48 +++++++++++++++++++++++++++--- crates/project/src/git.rs | 29 +++++++++++++++++- crates/project/src/project.rs | 30 ++++++++++++------- 3 files changed, 92 insertions(+), 15 deletions(-) diff --git a/crates/project/src/buffer_store.rs b/crates/project/src/buffer_store.rs index dc12a4d84477b65799a9800cadbe49c4fe6377e9..ab2ae7b5e5ed51ee7455c2cdcc5214a87e1f0403 100644 --- a/crates/project/src/buffer_store.rs +++ b/crates/project/src/buffer_store.rs @@ -136,6 +136,15 @@ impl BufferDiffState { let _ = self.diff_bases_changed(buffer, diff_bases_change, cx); } + pub fn wait_for_recalculation(&mut self) -> Option> { + if self.diff_updated_futures.is_empty() { + return None; + } + let (tx, rx) = oneshot::channel(); + self.diff_updated_futures.push(tx); + Some(rx) + } + fn diff_bases_changed( &mut self, buffer: text::BufferSnapshot, @@ -1362,8 +1371,23 @@ impl BufferStore { cx: &mut Context, ) -> Task>> { let buffer_id = buffer.read(cx).remote_id(); - if let Some(diff) = self.get_unstaged_diff(buffer_id, cx) { - return Task::ready(Ok(diff)); + if let Some(OpenBuffer::Complete { diff_state, .. }) = self.opened_buffers.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)) { @@ -1402,8 +1426,24 @@ impl BufferStore { cx: &mut Context, ) -> Task>> { let buffer_id = buffer.read(cx).remote_id(); - if let Some(diff) = self.get_uncommitted_diff(buffer_id, cx) { - return Task::ready(Ok(diff)); + + if let Some(OpenBuffer::Complete { diff_state, .. }) = self.opened_buffers.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()) + { + return cx.background_executor().spawn(async move { + task.await?; + Ok(uncommitted_diff) + }); + } + return Task::ready(Ok(uncommitted_diff)); + } } let task = match self.loading_diffs.entry((buffer_id, DiffKind::Uncommitted)) { diff --git a/crates/project/src/git.rs b/crates/project/src/git.rs index 0f1cc5a5562752c853a428043105e772b7e956d5..7263027179e823146ca64ca967048abe02070d18 100644 --- a/crates/project/src/git.rs +++ b/crates/project/src/git.rs @@ -21,7 +21,7 @@ use std::path::{Path, PathBuf}; use std::sync::Arc; use text::BufferId; use util::{maybe, ResultExt}; -use worktree::{ProjectEntryId, RepositoryEntry, StatusEntry}; +use worktree::{ProjectEntryId, RepositoryEntry, StatusEntry, WorkDirectory}; pub struct GitStore { buffer_store: Entity, @@ -688,6 +688,33 @@ impl Repository { self.worktree_id_path_to_repo_path(path.worktree_id, &path.path) } + // note: callers must verify these come from the same worktree + pub fn contains_sub_repo(&self, other: &Entity, cx: &App) -> bool { + let other_work_dir = &other.read(cx).repository_entry.work_directory; + match (&self.repository_entry.work_directory, other_work_dir) { + (WorkDirectory::InProject { .. }, WorkDirectory::AboveProject { .. }) => false, + (WorkDirectory::AboveProject { .. }, WorkDirectory::InProject { .. }) => true, + ( + WorkDirectory::InProject { + relative_path: this_path, + }, + WorkDirectory::InProject { + relative_path: other_path, + }, + ) => other_path.starts_with(this_path), + ( + WorkDirectory::AboveProject { + absolute_path: this_path, + .. + }, + WorkDirectory::AboveProject { + absolute_path: other_path, + .. + }, + ) => other_path.starts_with(this_path), + } + } + pub fn worktree_id_path_to_repo_path( &self, worktree_id: WorktreeId, diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index 08de17318196a395382bade33062403b32351842..b61466747bdfb99c1177a45b5017cc6ae89d9fdb 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -4310,16 +4310,26 @@ impl Project { .buffer_for_id(buffer_id, cx)? .read(cx) .project_path(cx)?; - self.git_store - .read(cx) - .all_repositories() - .into_iter() - .find_map(|repo| { - Some(( - repo.clone(), - repo.read(cx).repository_entry.relativize(&path.path).ok()?, - )) - }) + + let mut found: Option<(Entity, RepoPath)> = None; + for repo_handle in self.git_store.read(cx).all_repositories() { + let repo = repo_handle.read(cx); + if repo.worktree_id != path.worktree_id { + continue; + } + let Ok(relative_path) = repo.repository_entry.relativize(&path.path) else { + continue; + }; + if found + .as_ref() + .is_some_and(|(found, _)| repo.contains_sub_repo(found, cx)) + { + continue; + } + found = Some((repo_handle.clone(), relative_path)) + } + + found } }