diff --git a/crates/buffer_diff/src/buffer_diff.rs b/crates/buffer_diff/src/buffer_diff.rs index cc1767b4cb..2363567ce1 100644 --- a/crates/buffer_diff/src/buffer_diff.rs +++ b/crates/buffer_diff/src/buffer_diff.rs @@ -184,17 +184,31 @@ impl BufferDiffSnapshot { cx: &mut App, ) -> Option { let secondary_diff = self.secondary_diff()?; - let index_base = if let Some(index_base) = secondary_diff.base_text() { - index_base.text.as_rope().clone() - } else if stage { - Rope::from("") - } else { - return None; + let head_text = self.base_text().map(|text| text.as_rope().clone()); + let index_text = secondary_diff + .base_text() + .map(|text| text.as_rope().clone()); + let (index_text, head_text) = match (index_text, head_text) { + (Some(index_text), Some(head_text)) => (index_text, head_text), + // file is deleted in both index and head + (None, None) => return None, + // file is deleted in index + (None, Some(head_text)) => { + return if stage { + Some(buffer.as_rope().clone()) + } else { + Some(head_text) + } + } + // file exists in the index, but is deleted in head + (Some(_), None) => { + return if stage { + Some(buffer.as_rope().clone()) + } else { + None + } + } }; - let head_base = self.base_text().map_or_else( - || Rope::from(""), - |snapshot| snapshot.text.as_rope().clone(), - ); let mut secondary_cursor = secondary_diff.inner.hunks.cursor::(buffer); secondary_cursor.next(buffer); @@ -251,7 +265,7 @@ impl BufferDiffSnapshot { .collect::() } else { log::debug!("unstaging"); - head_base + head_text .chunks_in_range(diff_base_byte_range.clone()) .collect::() }; @@ -259,7 +273,7 @@ impl BufferDiffSnapshot { } let buffer = cx.new(|cx| { - language::Buffer::local_normalized(index_base, text::LineEnding::default(), cx) + language::Buffer::local_normalized(index_text, text::LineEnding::default(), cx) }); let new_text = buffer.update(cx, |buffer, cx| { buffer.edit(edits, None, cx); diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index 75c15f0e09..941e22f97c 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -102,9 +102,9 @@ use language::{ self, all_language_settings, language_settings, InlayHintSettings, RewrapBehavior, }, point_from_lsp, text_diff_with_options, AutoindentMode, BracketMatch, BracketPair, Buffer, - Capability, CharKind, CodeLabel, CursorShape, Diagnostic, DiffOptions, DiskState, - EditPredictionsMode, EditPreview, HighlightedText, IndentKind, IndentSize, Language, - OffsetRangeExt, Point, Selection, SelectionGoal, TextObject, TransactionId, TreeSitterOptions, + Capability, CharKind, CodeLabel, CursorShape, Diagnostic, DiffOptions, EditPredictionsMode, + EditPreview, HighlightedText, IndentKind, IndentSize, Language, OffsetRangeExt, Point, + Selection, SelectionGoal, TextObject, TransactionId, TreeSitterOptions, }; use language::{point_to_lsp, BufferRow, CharClassifier, Runnable, RunnableRange}; use linked_editing_ranges::refresh_linked_ranges; @@ -13483,13 +13483,16 @@ impl Editor { buffer_id: BufferId, hunks: impl Iterator, snapshot: &MultiBufferSnapshot, - cx: &mut Context, + cx: &mut App, ) { let Some(buffer) = project.read(cx).buffer_for_id(buffer_id, cx) else { log::debug!("no buffer for id"); return; }; let buffer_snapshot = buffer.read(cx).snapshot(); + let file_exists = buffer_snapshot + .file() + .is_some_and(|file| file.disk_state().exists()); let Some((repo, path)) = project .read(cx) .repository_and_path_for_buffer_id(buffer_id, cx) @@ -13502,40 +13505,33 @@ impl Editor { return; }; - let Some(new_index_text) = diff.new_secondary_text_for_stage_or_unstage( - stage, - hunks.filter_map(|hunk| { - if stage && hunk.secondary_status == DiffHunkSecondaryStatus::None { - return None; - } else if !stage - && hunk.secondary_status == DiffHunkSecondaryStatus::HasSecondaryHunk - { - return None; - } - Some((hunk.buffer_range.clone(), hunk.diff_base_byte_range.clone())) - }), - &buffer_snapshot, - cx, - ) else { - log::debug!("missing secondary diff or index text"); - return; - }; - let new_index_text = if new_index_text.is_empty() - && !stage - && (diff.is_single_insertion - || buffer_snapshot - .file() - .map_or(false, |file| file.disk_state() == DiskState::New)) - { + let new_index_text = if !stage && diff.is_single_insertion || stage && !file_exists { log::debug!("removing from index"); None } else { - Some(new_index_text) + diff.new_secondary_text_for_stage_or_unstage( + stage, + hunks.filter_map(|hunk| { + if stage && hunk.secondary_status == DiffHunkSecondaryStatus::None { + return None; + } else if !stage + && hunk.secondary_status == DiffHunkSecondaryStatus::HasSecondaryHunk + { + return None; + } + Some((hunk.buffer_range.clone(), hunk.diff_base_byte_range.clone())) + }), + &buffer_snapshot, + cx, + ) }; - let buffer_store = project.read(cx).buffer_store().clone(); - buffer_store - .update(cx, |buffer_store, cx| buffer_store.save_buffer(buffer, cx)) - .detach_and_log_err(cx); + + if file_exists { + let buffer_store = project.read(cx).buffer_store().clone(); + buffer_store + .update(cx, |buffer_store, cx| buffer_store.save_buffer(buffer, cx)) + .detach_and_log_err(cx); + } cx.background_spawn( repo.read(cx) diff --git a/crates/editor/src/editor_tests.rs b/crates/editor/src/editor_tests.rs index 8e81c2560b..36b9108bc4 100644 --- a/crates/editor/src/editor_tests.rs +++ b/crates/editor/src/editor_tests.rs @@ -16510,6 +16510,7 @@ fn assert_hunk_revert( ) { cx.set_state(not_reverted_text_with_selections); cx.set_head_text(base_text); + cx.clear_index_text(); cx.executor().run_until_parked(); let actual_hunk_statuses_before = cx.update_editor(|editor, window, cx| { diff --git a/crates/editor/src/test/editor_test_context.rs b/crates/editor/src/test/editor_test_context.rs index 6a08f6e283..18b9b7a916 100644 --- a/crates/editor/src/test/editor_test_context.rs +++ b/crates/editor/src/test/editor_test_context.rs @@ -298,6 +298,15 @@ impl EditorTestContext { self.cx.run_until_parked(); } + pub fn clear_index_text(&mut self) { + self.cx.run_until_parked(); + let fs = self.update_editor(|editor, _, cx| { + editor.project.as_ref().unwrap().read(cx).fs().as_fake() + }); + fs.set_index_for_repo(&Self::root_path().join(".git"), &[]); + self.cx.run_until_parked(); + } + pub fn set_index_text(&mut self, diff_base: &str) { self.cx.run_until_parked(); let fs = self.update_editor(|editor, _, cx| { diff --git a/crates/language/src/buffer.rs b/crates/language/src/buffer.rs index 14d2bb3a7c..c9719e8e6b 100644 --- a/crates/language/src/buffer.rs +++ b/crates/language/src/buffer.rs @@ -368,6 +368,14 @@ impl DiskState { DiskState::Deleted => None, } } + + pub fn exists(&self) -> bool { + match self { + DiskState::New => false, + DiskState::Present { .. } => true, + DiskState::Deleted => false, + } + } } /// The file associated with a buffer, in the case where the file is on the local disk. diff --git a/crates/project/src/git.rs b/crates/project/src/git.rs index a11b40c687..fb9c015c0e 100644 --- a/crates/project/src/git.rs +++ b/crates/project/src/git.rs @@ -1071,7 +1071,13 @@ impl Repository { }; let project_path = (self.worktree_id, path).into(); if let Some(buffer) = buffer_store.get_by_path(&project_path, cx) { - save_futures.push(buffer_store.save_buffer(buffer, cx)); + if buffer + .read(cx) + .file() + .map_or(false, |file| file.disk_state().exists()) + { + save_futures.push(buffer_store.save_buffer(buffer, cx)); + } } } }) @@ -1110,7 +1116,13 @@ impl Repository { }; let project_path = (self.worktree_id, path).into(); if let Some(buffer) = buffer_store.get_by_path(&project_path, cx) { - save_futures.push(buffer_store.save_buffer(buffer, cx)); + if buffer + .read(cx) + .file() + .map_or(false, |file| file.disk_state().exists()) + { + save_futures.push(buffer_store.save_buffer(buffer, cx)); + } } } })