Fix the project diff sometimes missing updates (#40662)
This PR does two related things: - First, it gets rid of the undifferentiated `RepositoryEvent::Updated` in favor of three new events that have clearer definitions: `BranchChanged`, `StashEntriesChanged`, and `StatusesChanged`. An implication of this is that we no longer emit a `RepositoryEvent` unless some git state changed; previously we would emit `RepositoryUpdated` after doing a git status scan even if no statuses changed. - Second, it changes the subscription strategy of the project diff to make it update more robustly. Previously, the project diff only subscribed to the `GitStore`, so it relied on getting a `GitStoreEvent` when some buffer's diff hunks changed, even if the git status of the buffer's file didn't change (e.g. a second hunk in a file that was already modified). After this PR, it also subscribes to the individual `BufferDiff` entities for buffers that have a git status, so the `GitStore` is freed from that responsibility. This also fixes some real cases where the previous strategy was not effective in keeping the project diff up to date (captured in a test). Release Notes: - Fixed some cases where the project diff would fail to update in response to git events.
This commit is contained in:
@@ -11,14 +11,20 @@ use git::{
|
||||
},
|
||||
status::{FileStatus, GitStatus, StatusCode, TrackedStatus, UnmergedStatus},
|
||||
};
|
||||
use gpui::{AsyncApp, BackgroundExecutor, SharedString, Task};
|
||||
use gpui::{AsyncApp, BackgroundExecutor, SharedString, Task, TaskLabel};
|
||||
use ignore::gitignore::GitignoreBuilder;
|
||||
use parking_lot::Mutex;
|
||||
use rope::Rope;
|
||||
use smol::future::FutureExt as _;
|
||||
use std::{path::PathBuf, sync::Arc};
|
||||
use std::{
|
||||
path::PathBuf,
|
||||
sync::{Arc, LazyLock},
|
||||
};
|
||||
use util::{paths::PathStyle, rel_path::RelPath};
|
||||
|
||||
pub static LOAD_INDEX_TEXT_TASK: LazyLock<TaskLabel> = LazyLock::new(TaskLabel::new);
|
||||
pub static LOAD_HEAD_TEXT_TASK: LazyLock<TaskLabel> = LazyLock::new(TaskLabel::new);
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct FakeGitRepository {
|
||||
pub(crate) fs: Arc<FakeFs>,
|
||||
@@ -79,33 +85,29 @@ impl GitRepository for FakeGitRepository {
|
||||
fn reload_index(&self) {}
|
||||
|
||||
fn load_index_text(&self, path: RepoPath) -> BoxFuture<'_, Option<String>> {
|
||||
async {
|
||||
self.with_state_async(false, move |state| {
|
||||
state
|
||||
.index_contents
|
||||
.get(&path)
|
||||
.context("not present in index")
|
||||
.cloned()
|
||||
})
|
||||
.await
|
||||
.ok()
|
||||
}
|
||||
.boxed()
|
||||
let fut = self.with_state_async(false, move |state| {
|
||||
state
|
||||
.index_contents
|
||||
.get(&path)
|
||||
.context("not present in index")
|
||||
.cloned()
|
||||
});
|
||||
self.executor
|
||||
.spawn_labeled(*LOAD_INDEX_TEXT_TASK, async move { fut.await.ok() })
|
||||
.boxed()
|
||||
}
|
||||
|
||||
fn load_committed_text(&self, path: RepoPath) -> BoxFuture<'_, Option<String>> {
|
||||
async {
|
||||
self.with_state_async(false, move |state| {
|
||||
state
|
||||
.head_contents
|
||||
.get(&path)
|
||||
.context("not present in HEAD")
|
||||
.cloned()
|
||||
})
|
||||
.await
|
||||
.ok()
|
||||
}
|
||||
.boxed()
|
||||
let fut = self.with_state_async(false, move |state| {
|
||||
state
|
||||
.head_contents
|
||||
.get(&path)
|
||||
.context("not present in HEAD")
|
||||
.cloned()
|
||||
});
|
||||
self.executor
|
||||
.spawn_labeled(*LOAD_HEAD_TEXT_TASK, async move { fut.await.ok() })
|
||||
.boxed()
|
||||
}
|
||||
|
||||
fn load_commit(
|
||||
|
||||
@@ -58,6 +58,9 @@ use smol::io::AsyncReadExt;
|
||||
#[cfg(any(test, feature = "test-support"))]
|
||||
use std::ffi::OsStr;
|
||||
|
||||
#[cfg(any(test, feature = "test-support"))]
|
||||
pub use fake_git_repo::{LOAD_HEAD_TEXT_TASK, LOAD_INDEX_TEXT_TASK};
|
||||
|
||||
pub trait Watcher: Send + Sync {
|
||||
fn add(&self, path: &Path) -> Result<()>;
|
||||
fn remove(&self, path: &Path) -> Result<()>;
|
||||
|
||||
Reference in New Issue
Block a user