From 922ee36601c8cc39e4dcc4d858f6d0182c983c0f Mon Sep 17 00:00:00 2001 From: Cole Miller Date: Fri, 20 Feb 2026 11:39:38 -0500 Subject: [PATCH] git: Prevent panic when updating excerpts in split diff (cherry-pick #49725) (#49731) When using the split view, if the project diff inserts excerpts for the same buffer using two different `PathKey`s, we panic inside `LhsEditor::update_path_excerpts_from_rhs` because of an implicit assumption that `excerpts_for_path()` and `excerpts_for_buffer()` return the same number of values. This PR addresses that by ignoring any excerpts for the given buffer that have a different path key from the provided one. Release Notes: - Fixed a crash when using the split diff view. Co-authored-by: Jakub Konka Co-authored-by: Eric --- crates/editor/src/split.rs | 62 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 59 insertions(+), 3 deletions(-) diff --git a/crates/editor/src/split.rs b/crates/editor/src/split.rs index 908438319263fbfe0db6842b2ee848aa418be1a7..f3ba7153afcfb8d0179f8c45527ffb3c02566c08 100644 --- a/crates/editor/src/split.rs +++ b/crates/editor/src/split.rs @@ -2033,6 +2033,7 @@ impl LhsEditor { let new = rhs_multibuffer .excerpts_for_buffer(main_buffer.remote_id(), lhs_cx) .into_iter() + .filter(|(id, _)| rhs_excerpt_ids.contains(&id)) .map(|(_, excerpt_range)| { let point_range_to_base_text_point_range = |range: Range| { let start = diff_snapshot @@ -2076,14 +2077,14 @@ impl LhsEditor { let mut current_group = Vec::new(); let mut last_id = None; - for (i, &lhs_id) in lhs_result.excerpt_ids.iter().enumerate() { + for (lhs_id, rhs_id) in lhs_result.excerpt_ids.iter().zip(rhs_excerpt_ids) { if last_id == Some(lhs_id) { - current_group.push(rhs_excerpt_ids[i]); + current_group.push(rhs_id); } else { if !current_group.is_empty() { groups.push(current_group); } - current_group = vec![rhs_excerpt_ids[i]]; + current_group = vec![rhs_id]; last_id = Some(lhs_id); } } @@ -2144,6 +2145,7 @@ mod tests { use rand::rngs::StdRng; use settings::{DiffViewStyle, SettingsStore}; use ui::{VisualContext as _, div, px}; + use util::rel_path::rel_path; use workspace::Workspace; use crate::SplittableEditor; @@ -5857,4 +5859,58 @@ mod tests { ); }); } + + #[gpui::test] + async fn test_two_path_keys_for_one_buffer(cx: &mut gpui::TestAppContext) { + use multi_buffer::PathKey; + use rope::Point; + use unindent::Unindent as _; + + let (editor, mut cx) = init_test(cx, SoftWrap::None, DiffViewStyle::Split).await; + + let base_text = " + aaa + bbb + ccc + " + .unindent(); + let current_text = " + aaa + bbb modified + ccc + " + .unindent(); + + let (buffer, diff) = buffer_with_diff(&base_text, ¤t_text, &mut cx); + + let path_key_1 = PathKey { + sort_prefix: Some(0), + path: rel_path("file1.txt").into(), + }; + let path_key_2 = PathKey { + sort_prefix: Some(1), + path: rel_path("file1.txt").into(), + }; + + editor.update(cx, |editor, cx| { + editor.set_excerpts_for_path( + path_key_1.clone(), + buffer.clone(), + vec![Point::new(0, 0)..Point::new(1, 0)], + 0, + diff.clone(), + cx, + ); + editor.set_excerpts_for_path( + path_key_2.clone(), + buffer.clone(), + vec![Point::new(1, 0)..buffer.read(cx).max_point()], + 1, + diff.clone(), + cx, + ); + }); + + cx.run_until_parked(); + } }