git: Improve diff performance (#54435)

Previously, passing an empty range to `range_to_buffer_range` would
return no ranges, which would trigger a fallback case in the git diff
that caused the entire multibuffer to be invalidated every frame.

This PR fixes `range_to_buffer_range`, so that it returns a sensible
range if the input range is empty, preventing the fallback behaviour and
edit expansion.

Self-Review Checklist:

- [ ] I've reviewed my own diff for quality, security, and reliability
- [ ] Unsafe blocks (if any) have justifying comments
- [ ] The content is consistent with the [UI/UX
checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist)
- [ ] Tests cover the new/changed behavior
- [ ] Performance impact has been considered and is acceptable

Closes #ISSUE

Release Notes:

- N/A or Added/Fixed/Improved ...

---------

Co-authored-by: Cole Miller <cole@zed.dev>
Co-authored-by: Anthony Eid <hello@anthonyeid.me>
This commit is contained in:
authored and GitHub committed 2026-04-22 02:42:11 +00:00
1 parent 310e367448
commit 3cd0f4c419
6 files changed
+91 -41

No files matched your search

+10 -8
View File
@@ -6,7 +6,7 @@ use crate::{
use agent_settings::AgentSettings;
use anyhow::{Context as _, Result, anyhow};
use buffer_diff::{BufferDiff, DiffHunkSecondaryStatus};
use collections::{HashMap, HashSet};
use collections::HashMap;
use editor::{
Addon, Editor, EditorEvent, EditorSettings, SelectionEffects, SplittableEditor,
actions::{GoToHunk, GoToPreviousHunk, SendReviewToAgent},
@@ -768,7 +768,7 @@ impl ProjectDiff {
needs_fold
}
#[instrument(skip_all)]
#[instrument(skip(this, cx))]
pub async fn refresh(
this: WeakEntity<Self>,
reason: RefreshReason,
@@ -780,13 +780,13 @@ impl ProjectDiff {
let load_buffers = branch_diff.load_buffers(cx);
(branch_diff.repo().cloned(), load_buffers)
});
let mut previous_paths = this
let mut previous_buffers = this
.multibuffer
.read(cx)
.snapshot(cx)
.buffers_with_paths()
.map(|(_, path_key)| path_key.clone())
.collect::<HashSet<_>>();
.map(|(buffer_snapshot, path_key)| (path_key.clone(), buffer_snapshot.remote_id()))
.collect::<HashMap<_, _>>();
if let Some(repo) = repo {
let repo = repo.read(cx);
@@ -796,14 +796,14 @@ impl ProjectDiff {
let sort_prefix = sort_prefix(&repo, &entry.repo_path, entry.file_status, cx);
let path_key =
PathKey::with_sort_prefix(sort_prefix, entry.repo_path.as_ref().clone());
previous_paths.remove(&path_key);
previous_buffers.remove(&path_key);
path_keys.push(path_key)
}
}
this.editor.update(cx, |editor, cx| {
for path in previous_paths {
if let Some(buffer) = this.multibuffer.read(cx).buffer_for_path(&path, cx) {
for (path, buffer_id) in previous_buffers {
if let Some(buffer) = this.multibuffer.read(cx).buffer(buffer_id) {
let skip = match reason {
RefreshReason::DiffChanged | RefreshReason::EditorSaved => {
buffer.read(cx).is_dirty()
@@ -816,6 +816,8 @@ impl ProjectDiff {
}
this.buffer_diff_subscriptions.remove(&path.path);
let _span = ztracing::info_span!("remove_excerpts_for_path");
_span.enter();
editor.remove_excerpts_for_path(path, cx);
}
});