Fix staging and unstaging of added and deleted files (#25631)
* When staging in a buffer whose file has been deleted, do not save the file * Fix logic for writing to index when file is deleted Release Notes: - N/A
This commit is contained in:
parent
33754f8eac
commit
ebccef1aa4
6 changed files with 88 additions and 48 deletions
|
@ -184,17 +184,31 @@ impl BufferDiffSnapshot {
|
||||||
cx: &mut App,
|
cx: &mut App,
|
||||||
) -> Option<Rope> {
|
) -> Option<Rope> {
|
||||||
let secondary_diff = self.secondary_diff()?;
|
let secondary_diff = self.secondary_diff()?;
|
||||||
let index_base = if let Some(index_base) = secondary_diff.base_text() {
|
let head_text = self.base_text().map(|text| text.as_rope().clone());
|
||||||
index_base.text.as_rope().clone()
|
let index_text = secondary_diff
|
||||||
} else if stage {
|
.base_text()
|
||||||
Rope::from("")
|
.map(|text| text.as_rope().clone());
|
||||||
} else {
|
let (index_text, head_text) = match (index_text, head_text) {
|
||||||
return None;
|
(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::<DiffHunkSummary>(buffer);
|
let mut secondary_cursor = secondary_diff.inner.hunks.cursor::<DiffHunkSummary>(buffer);
|
||||||
secondary_cursor.next(buffer);
|
secondary_cursor.next(buffer);
|
||||||
|
@ -251,7 +265,7 @@ impl BufferDiffSnapshot {
|
||||||
.collect::<String>()
|
.collect::<String>()
|
||||||
} else {
|
} else {
|
||||||
log::debug!("unstaging");
|
log::debug!("unstaging");
|
||||||
head_base
|
head_text
|
||||||
.chunks_in_range(diff_base_byte_range.clone())
|
.chunks_in_range(diff_base_byte_range.clone())
|
||||||
.collect::<String>()
|
.collect::<String>()
|
||||||
};
|
};
|
||||||
|
@ -259,7 +273,7 @@ impl BufferDiffSnapshot {
|
||||||
}
|
}
|
||||||
|
|
||||||
let buffer = cx.new(|cx| {
|
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| {
|
let new_text = buffer.update(cx, |buffer, cx| {
|
||||||
buffer.edit(edits, None, cx);
|
buffer.edit(edits, None, cx);
|
||||||
|
|
|
@ -102,9 +102,9 @@ use language::{
|
||||||
self, all_language_settings, language_settings, InlayHintSettings, RewrapBehavior,
|
self, all_language_settings, language_settings, InlayHintSettings, RewrapBehavior,
|
||||||
},
|
},
|
||||||
point_from_lsp, text_diff_with_options, AutoindentMode, BracketMatch, BracketPair, Buffer,
|
point_from_lsp, text_diff_with_options, AutoindentMode, BracketMatch, BracketPair, Buffer,
|
||||||
Capability, CharKind, CodeLabel, CursorShape, Diagnostic, DiffOptions, DiskState,
|
Capability, CharKind, CodeLabel, CursorShape, Diagnostic, DiffOptions, EditPredictionsMode,
|
||||||
EditPredictionsMode, EditPreview, HighlightedText, IndentKind, IndentSize, Language,
|
EditPreview, HighlightedText, IndentKind, IndentSize, Language, OffsetRangeExt, Point,
|
||||||
OffsetRangeExt, Point, Selection, SelectionGoal, TextObject, TransactionId, TreeSitterOptions,
|
Selection, SelectionGoal, TextObject, TransactionId, TreeSitterOptions,
|
||||||
};
|
};
|
||||||
use language::{point_to_lsp, BufferRow, CharClassifier, Runnable, RunnableRange};
|
use language::{point_to_lsp, BufferRow, CharClassifier, Runnable, RunnableRange};
|
||||||
use linked_editing_ranges::refresh_linked_ranges;
|
use linked_editing_ranges::refresh_linked_ranges;
|
||||||
|
@ -13483,13 +13483,16 @@ impl Editor {
|
||||||
buffer_id: BufferId,
|
buffer_id: BufferId,
|
||||||
hunks: impl Iterator<Item = MultiBufferDiffHunk>,
|
hunks: impl Iterator<Item = MultiBufferDiffHunk>,
|
||||||
snapshot: &MultiBufferSnapshot,
|
snapshot: &MultiBufferSnapshot,
|
||||||
cx: &mut Context<Self>,
|
cx: &mut App,
|
||||||
) {
|
) {
|
||||||
let Some(buffer) = project.read(cx).buffer_for_id(buffer_id, cx) else {
|
let Some(buffer) = project.read(cx).buffer_for_id(buffer_id, cx) else {
|
||||||
log::debug!("no buffer for id");
|
log::debug!("no buffer for id");
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
let buffer_snapshot = buffer.read(cx).snapshot();
|
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
|
let Some((repo, path)) = project
|
||||||
.read(cx)
|
.read(cx)
|
||||||
.repository_and_path_for_buffer_id(buffer_id, cx)
|
.repository_and_path_for_buffer_id(buffer_id, cx)
|
||||||
|
@ -13502,40 +13505,33 @@ impl Editor {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
let Some(new_index_text) = diff.new_secondary_text_for_stage_or_unstage(
|
let new_index_text = if !stage && diff.is_single_insertion || stage && !file_exists {
|
||||||
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))
|
|
||||||
{
|
|
||||||
log::debug!("removing from index");
|
log::debug!("removing from index");
|
||||||
None
|
None
|
||||||
} else {
|
} 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
|
if file_exists {
|
||||||
.update(cx, |buffer_store, cx| buffer_store.save_buffer(buffer, cx))
|
let buffer_store = project.read(cx).buffer_store().clone();
|
||||||
.detach_and_log_err(cx);
|
buffer_store
|
||||||
|
.update(cx, |buffer_store, cx| buffer_store.save_buffer(buffer, cx))
|
||||||
|
.detach_and_log_err(cx);
|
||||||
|
}
|
||||||
|
|
||||||
cx.background_spawn(
|
cx.background_spawn(
|
||||||
repo.read(cx)
|
repo.read(cx)
|
||||||
|
|
|
@ -16510,6 +16510,7 @@ fn assert_hunk_revert(
|
||||||
) {
|
) {
|
||||||
cx.set_state(not_reverted_text_with_selections);
|
cx.set_state(not_reverted_text_with_selections);
|
||||||
cx.set_head_text(base_text);
|
cx.set_head_text(base_text);
|
||||||
|
cx.clear_index_text();
|
||||||
cx.executor().run_until_parked();
|
cx.executor().run_until_parked();
|
||||||
|
|
||||||
let actual_hunk_statuses_before = cx.update_editor(|editor, window, cx| {
|
let actual_hunk_statuses_before = cx.update_editor(|editor, window, cx| {
|
||||||
|
|
|
@ -298,6 +298,15 @@ impl EditorTestContext {
|
||||||
self.cx.run_until_parked();
|
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) {
|
pub fn set_index_text(&mut self, diff_base: &str) {
|
||||||
self.cx.run_until_parked();
|
self.cx.run_until_parked();
|
||||||
let fs = self.update_editor(|editor, _, cx| {
|
let fs = self.update_editor(|editor, _, cx| {
|
||||||
|
|
|
@ -368,6 +368,14 @@ impl DiskState {
|
||||||
DiskState::Deleted => None,
|
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.
|
/// The file associated with a buffer, in the case where the file is on the local disk.
|
||||||
|
|
|
@ -1071,7 +1071,13 @@ impl Repository {
|
||||||
};
|
};
|
||||||
let project_path = (self.worktree_id, path).into();
|
let project_path = (self.worktree_id, path).into();
|
||||||
if let Some(buffer) = buffer_store.get_by_path(&project_path, cx) {
|
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();
|
let project_path = (self.worktree_id, path).into();
|
||||||
if let Some(buffer) = buffer_store.get_by_path(&project_path, cx) {
|
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));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue