Git: Fix hunks being skipped when staging too quickly (#27552)
Release Notes: - Git: Fix hunks being skipped when staging too quickly. --------- Co-authored-by: Max Brunsfeld <maxbrunsfeld@gmail.com>
This commit is contained in:
parent
29e2e13e6d
commit
47b94e5ef0
2 changed files with 83 additions and 53 deletions
|
@ -187,13 +187,13 @@ impl BufferDiffSnapshot {
|
||||||
impl BufferDiffInner {
|
impl BufferDiffInner {
|
||||||
/// Returns the new index text and new pending hunks.
|
/// Returns the new index text and new pending hunks.
|
||||||
fn stage_or_unstage_hunks_impl(
|
fn stage_or_unstage_hunks_impl(
|
||||||
&self,
|
&mut self,
|
||||||
unstaged_diff: &Self,
|
unstaged_diff: &Self,
|
||||||
stage: bool,
|
stage: bool,
|
||||||
hunks: &[DiffHunk],
|
hunks: &[DiffHunk],
|
||||||
buffer: &text::BufferSnapshot,
|
buffer: &text::BufferSnapshot,
|
||||||
file_exists: bool,
|
file_exists: bool,
|
||||||
) -> (Option<Rope>, SumTree<PendingHunk>) {
|
) -> Option<Rope> {
|
||||||
let head_text = self
|
let head_text = self
|
||||||
.base_text_exists
|
.base_text_exists
|
||||||
.then(|| self.base_text.as_rope().clone());
|
.then(|| self.base_text.as_rope().clone());
|
||||||
|
@ -206,7 +206,7 @@ impl BufferDiffInner {
|
||||||
let (index_text, head_text) = match (index_text, head_text) {
|
let (index_text, head_text) = match (index_text, head_text) {
|
||||||
(Some(index_text), Some(head_text)) if file_exists || !stage => (index_text, head_text),
|
(Some(index_text), Some(head_text)) if file_exists || !stage => (index_text, head_text),
|
||||||
(index_text, head_text) => {
|
(index_text, head_text) => {
|
||||||
let (rope, new_status) = if stage {
|
let (new_index_text, new_status) = if stage {
|
||||||
log::debug!("stage all");
|
log::debug!("stage all");
|
||||||
(
|
(
|
||||||
file_exists.then(|| buffer.as_rope().clone()),
|
file_exists.then(|| buffer.as_rope().clone()),
|
||||||
|
@ -226,15 +226,13 @@ impl BufferDiffInner {
|
||||||
buffer_version: buffer.version().clone(),
|
buffer_version: buffer.version().clone(),
|
||||||
new_status,
|
new_status,
|
||||||
};
|
};
|
||||||
let tree = SumTree::from_item(hunk, buffer);
|
self.pending_hunks = SumTree::from_item(hunk, buffer);
|
||||||
return (rope, tree);
|
return new_index_text;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut pending_hunks = SumTree::new(buffer);
|
let mut pending_hunks = SumTree::new(buffer);
|
||||||
let mut old_pending_hunks = unstaged_diff
|
let mut old_pending_hunks = self.pending_hunks.cursor::<DiffHunkSummary>(buffer);
|
||||||
.pending_hunks
|
|
||||||
.cursor::<DiffHunkSummary>(buffer);
|
|
||||||
|
|
||||||
// first, merge new hunks into pending_hunks
|
// first, merge new hunks into pending_hunks
|
||||||
for DiffHunk {
|
for DiffHunk {
|
||||||
|
@ -366,6 +364,8 @@ impl BufferDiffInner {
|
||||||
edits.push((index_byte_range, replacement_text));
|
edits.push((index_byte_range, replacement_text));
|
||||||
}
|
}
|
||||||
drop(pending_hunks_iter);
|
drop(pending_hunks_iter);
|
||||||
|
drop(old_pending_hunks);
|
||||||
|
self.pending_hunks = pending_hunks;
|
||||||
|
|
||||||
#[cfg(debug_assertions)] // invariants: non-overlapping and sorted
|
#[cfg(debug_assertions)] // invariants: non-overlapping and sorted
|
||||||
{
|
{
|
||||||
|
@ -384,7 +384,7 @@ impl BufferDiffInner {
|
||||||
new_index_text.push(&replacement_text);
|
new_index_text.push(&replacement_text);
|
||||||
}
|
}
|
||||||
new_index_text.append(index_cursor.suffix());
|
new_index_text.append(index_cursor.suffix());
|
||||||
(Some(new_index_text), pending_hunks)
|
Some(new_index_text)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn hunks_intersecting_range<'a>(
|
fn hunks_intersecting_range<'a>(
|
||||||
|
@ -421,15 +421,14 @@ impl BufferDiffInner {
|
||||||
]
|
]
|
||||||
});
|
});
|
||||||
|
|
||||||
|
let mut pending_hunks_cursor = self.pending_hunks.cursor::<DiffHunkSummary>(buffer);
|
||||||
|
pending_hunks_cursor.next(buffer);
|
||||||
|
|
||||||
let mut secondary_cursor = None;
|
let mut secondary_cursor = None;
|
||||||
let mut pending_hunks_cursor = None;
|
|
||||||
if let Some(secondary) = secondary.as_ref() {
|
if let Some(secondary) = secondary.as_ref() {
|
||||||
let mut cursor = secondary.hunks.cursor::<DiffHunkSummary>(buffer);
|
let mut cursor = secondary.hunks.cursor::<DiffHunkSummary>(buffer);
|
||||||
cursor.next(buffer);
|
cursor.next(buffer);
|
||||||
secondary_cursor = Some(cursor);
|
secondary_cursor = Some(cursor);
|
||||||
let mut cursor = secondary.pending_hunks.cursor::<DiffHunkSummary>(buffer);
|
|
||||||
cursor.next(buffer);
|
|
||||||
pending_hunks_cursor = Some(cursor);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let max_point = buffer.max_point();
|
let max_point = buffer.max_point();
|
||||||
|
@ -451,29 +450,27 @@ impl BufferDiffInner {
|
||||||
let mut secondary_status = DiffHunkSecondaryStatus::NoSecondaryHunk;
|
let mut secondary_status = DiffHunkSecondaryStatus::NoSecondaryHunk;
|
||||||
|
|
||||||
let mut has_pending = false;
|
let mut has_pending = false;
|
||||||
if let Some(pending_cursor) = pending_hunks_cursor.as_mut() {
|
if start_anchor
|
||||||
if start_anchor
|
.cmp(&pending_hunks_cursor.start().buffer_range.start, buffer)
|
||||||
.cmp(&pending_cursor.start().buffer_range.start, buffer)
|
.is_gt()
|
||||||
.is_gt()
|
{
|
||||||
{
|
pending_hunks_cursor.seek_forward(&start_anchor, Bias::Left, buffer);
|
||||||
pending_cursor.seek_forward(&start_anchor, Bias::Left, buffer);
|
}
|
||||||
|
|
||||||
|
if let Some(pending_hunk) = pending_hunks_cursor.item() {
|
||||||
|
let mut pending_range = pending_hunk.buffer_range.to_point(buffer);
|
||||||
|
if pending_range.end.column > 0 {
|
||||||
|
pending_range.end.row += 1;
|
||||||
|
pending_range.end.column = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(pending_hunk) = pending_cursor.item() {
|
if pending_range == (start_point..end_point) {
|
||||||
let mut pending_range = pending_hunk.buffer_range.to_point(buffer);
|
if !buffer.has_edits_since_in_range(
|
||||||
if pending_range.end.column > 0 {
|
&pending_hunk.buffer_version,
|
||||||
pending_range.end.row += 1;
|
start_anchor..end_anchor,
|
||||||
pending_range.end.column = 0;
|
) {
|
||||||
}
|
has_pending = true;
|
||||||
|
secondary_status = pending_hunk.new_status;
|
||||||
if pending_range == (start_point..end_point) {
|
|
||||||
if !buffer.has_edits_since_in_range(
|
|
||||||
&pending_hunk.buffer_version,
|
|
||||||
start_anchor..end_anchor,
|
|
||||||
) {
|
|
||||||
has_pending = true;
|
|
||||||
secondary_status = pending_hunk.new_status;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -852,10 +849,8 @@ impl BufferDiff {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn clear_pending_hunks(&mut self, cx: &mut Context<Self>) {
|
pub fn clear_pending_hunks(&mut self, cx: &mut Context<Self>) {
|
||||||
if let Some(secondary_diff) = &self.secondary_diff {
|
if self.secondary_diff.is_some() {
|
||||||
secondary_diff.update(cx, |diff, _| {
|
self.inner.pending_hunks = SumTree::from_summary(DiffHunkSummary::default());
|
||||||
diff.inner.pending_hunks = SumTree::from_summary(DiffHunkSummary::default());
|
|
||||||
});
|
|
||||||
cx.emit(BufferDiffEvent::DiffChanged {
|
cx.emit(BufferDiffEvent::DiffChanged {
|
||||||
changed_range: Some(Anchor::MIN..Anchor::MAX),
|
changed_range: Some(Anchor::MIN..Anchor::MAX),
|
||||||
});
|
});
|
||||||
|
@ -870,7 +865,7 @@ impl BufferDiff {
|
||||||
file_exists: bool,
|
file_exists: bool,
|
||||||
cx: &mut Context<Self>,
|
cx: &mut Context<Self>,
|
||||||
) -> Option<Rope> {
|
) -> Option<Rope> {
|
||||||
let (new_index_text, new_pending_hunks) = self.inner.stage_or_unstage_hunks_impl(
|
let new_index_text = self.inner.stage_or_unstage_hunks_impl(
|
||||||
&self.secondary_diff.as_ref()?.read(cx).inner,
|
&self.secondary_diff.as_ref()?.read(cx).inner,
|
||||||
stage,
|
stage,
|
||||||
&hunks,
|
&hunks,
|
||||||
|
@ -878,11 +873,6 @@ impl BufferDiff {
|
||||||
file_exists,
|
file_exists,
|
||||||
);
|
);
|
||||||
|
|
||||||
if let Some(unstaged_diff) = &self.secondary_diff {
|
|
||||||
unstaged_diff.update(cx, |diff, _| {
|
|
||||||
diff.inner.pending_hunks = new_pending_hunks;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
cx.emit(BufferDiffEvent::HunksStagedOrUnstaged(
|
cx.emit(BufferDiffEvent::HunksStagedOrUnstaged(
|
||||||
new_index_text.clone(),
|
new_index_text.clone(),
|
||||||
));
|
));
|
||||||
|
|
|
@ -85,6 +85,7 @@ struct BufferDiffState {
|
||||||
language: Option<Arc<Language>>,
|
language: Option<Arc<Language>>,
|
||||||
language_registry: Option<Arc<LanguageRegistry>>,
|
language_registry: Option<Arc<LanguageRegistry>>,
|
||||||
diff_updated_futures: Vec<oneshot::Sender<()>>,
|
diff_updated_futures: Vec<oneshot::Sender<()>>,
|
||||||
|
hunk_staging_operation_count: usize,
|
||||||
|
|
||||||
head_text: Option<Arc<String>>,
|
head_text: Option<Arc<String>>,
|
||||||
index_text: Option<Arc<String>>,
|
index_text: Option<Arc<String>>,
|
||||||
|
@ -574,7 +575,7 @@ impl GitStore {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let rx = diff_state.diff_bases_changed(text_snapshot, diff_bases_change, cx);
|
let rx = diff_state.diff_bases_changed(text_snapshot, diff_bases_change, 0, cx);
|
||||||
|
|
||||||
anyhow::Ok(async move {
|
anyhow::Ok(async move {
|
||||||
rx.await.ok();
|
rx.await.ok();
|
||||||
|
@ -1140,7 +1141,11 @@ impl GitStore {
|
||||||
if let Some(diff_state) = self.diffs.get_mut(&buffer.read(cx).remote_id()) {
|
if let Some(diff_state) = self.diffs.get_mut(&buffer.read(cx).remote_id()) {
|
||||||
let buffer = buffer.read(cx).text_snapshot();
|
let buffer = buffer.read(cx).text_snapshot();
|
||||||
futures.push(diff_state.update(cx, |diff_state, cx| {
|
futures.push(diff_state.update(cx, |diff_state, cx| {
|
||||||
diff_state.recalculate_diffs(buffer, cx)
|
diff_state.recalculate_diffs(
|
||||||
|
buffer,
|
||||||
|
diff_state.hunk_staging_operation_count,
|
||||||
|
cx,
|
||||||
|
)
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1157,6 +1162,11 @@ impl GitStore {
|
||||||
) {
|
) {
|
||||||
if let BufferDiffEvent::HunksStagedOrUnstaged(new_index_text) = event {
|
if let BufferDiffEvent::HunksStagedOrUnstaged(new_index_text) = event {
|
||||||
let buffer_id = diff.read(cx).buffer_id;
|
let buffer_id = diff.read(cx).buffer_id;
|
||||||
|
if let Some(diff_state) = self.diffs.get(&buffer_id) {
|
||||||
|
diff_state.update(cx, |diff_state, _| {
|
||||||
|
diff_state.hunk_staging_operation_count += 1;
|
||||||
|
});
|
||||||
|
}
|
||||||
if let Some((repo, path)) = self.repository_and_path_for_buffer_id(buffer_id, cx) {
|
if let Some((repo, path)) = self.repository_and_path_for_buffer_id(buffer_id, cx) {
|
||||||
let recv = repo.update(cx, |repo, cx| {
|
let recv = repo.update(cx, |repo, cx| {
|
||||||
log::debug!("updating index text for buffer {}", path.display());
|
log::debug!("updating index text for buffer {}", path.display());
|
||||||
|
@ -1229,6 +1239,7 @@ impl GitStore {
|
||||||
file.path.clone(),
|
file.path.clone(),
|
||||||
has_unstaged_diff.then(|| diff_state.index_text.clone()),
|
has_unstaged_diff.then(|| diff_state.index_text.clone()),
|
||||||
has_uncommitted_diff.then(|| diff_state.head_text.clone()),
|
has_uncommitted_diff.then(|| diff_state.head_text.clone()),
|
||||||
|
diff_state.hunk_staging_operation_count,
|
||||||
);
|
);
|
||||||
diff_state_updates.entry(repo_id).or_default().push(update);
|
diff_state_updates.entry(repo_id).or_default().push(update);
|
||||||
}
|
}
|
||||||
|
@ -1252,8 +1263,13 @@ impl GitStore {
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut diff_bases_changes_by_buffer = Vec::new();
|
let mut diff_bases_changes_by_buffer = Vec::new();
|
||||||
for (buffer, path, current_index_text, current_head_text) in
|
for (
|
||||||
&repo_diff_state_updates
|
buffer,
|
||||||
|
path,
|
||||||
|
current_index_text,
|
||||||
|
current_head_text,
|
||||||
|
hunk_staging_operation_count,
|
||||||
|
) in &repo_diff_state_updates
|
||||||
{
|
{
|
||||||
let Some(local_repo) = snapshot.local_repo_containing_path(&path) else {
|
let Some(local_repo) = snapshot.local_repo_containing_path(&path) else {
|
||||||
continue;
|
continue;
|
||||||
|
@ -1306,12 +1322,18 @@ impl GitStore {
|
||||||
(false, false) => None,
|
(false, false) => None,
|
||||||
};
|
};
|
||||||
|
|
||||||
diff_bases_changes_by_buffer.push((buffer, diff_bases_change))
|
diff_bases_changes_by_buffer.push((
|
||||||
|
buffer,
|
||||||
|
diff_bases_change,
|
||||||
|
*hunk_staging_operation_count,
|
||||||
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
git_store
|
git_store
|
||||||
.update(&mut cx, |git_store, cx| {
|
.update(&mut cx, |git_store, cx| {
|
||||||
for (buffer, diff_bases_change) in diff_bases_changes_by_buffer {
|
for (buffer, diff_bases_change, hunk_staging_operation_count) in
|
||||||
|
diff_bases_changes_by_buffer
|
||||||
|
{
|
||||||
let Some(diff_state) =
|
let Some(diff_state) =
|
||||||
git_store.diffs.get(&buffer.read(cx).remote_id())
|
git_store.diffs.get(&buffer.read(cx).remote_id())
|
||||||
else {
|
else {
|
||||||
|
@ -1356,6 +1378,7 @@ impl GitStore {
|
||||||
let _ = diff_state.diff_bases_changed(
|
let _ = diff_state.diff_bases_changed(
|
||||||
buffer.text_snapshot(),
|
buffer.text_snapshot(),
|
||||||
diff_bases_change,
|
diff_bases_change,
|
||||||
|
hunk_staging_operation_count,
|
||||||
cx,
|
cx,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
@ -2192,7 +2215,11 @@ impl BufferDiffState {
|
||||||
fn buffer_language_changed(&mut self, buffer: Entity<Buffer>, cx: &mut Context<Self>) {
|
fn buffer_language_changed(&mut self, buffer: Entity<Buffer>, cx: &mut Context<Self>) {
|
||||||
self.language = buffer.read(cx).language().cloned();
|
self.language = buffer.read(cx).language().cloned();
|
||||||
self.language_changed = true;
|
self.language_changed = true;
|
||||||
let _ = self.recalculate_diffs(buffer.read(cx).text_snapshot(), cx);
|
let _ = self.recalculate_diffs(
|
||||||
|
buffer.read(cx).text_snapshot(),
|
||||||
|
self.hunk_staging_operation_count,
|
||||||
|
cx,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn unstaged_diff(&self) -> Option<Entity<BufferDiff>> {
|
fn unstaged_diff(&self) -> Option<Entity<BufferDiff>> {
|
||||||
|
@ -2225,7 +2252,12 @@ impl BufferDiffState {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
let _ = self.diff_bases_changed(buffer, diff_bases_change, cx);
|
let _ = self.diff_bases_changed(
|
||||||
|
buffer,
|
||||||
|
diff_bases_change,
|
||||||
|
self.hunk_staging_operation_count,
|
||||||
|
cx,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn wait_for_recalculation(&mut self) -> Option<oneshot::Receiver<()>> {
|
pub fn wait_for_recalculation(&mut self) -> Option<oneshot::Receiver<()>> {
|
||||||
|
@ -2241,6 +2273,7 @@ impl BufferDiffState {
|
||||||
&mut self,
|
&mut self,
|
||||||
buffer: text::BufferSnapshot,
|
buffer: text::BufferSnapshot,
|
||||||
diff_bases_change: DiffBasesChange,
|
diff_bases_change: DiffBasesChange,
|
||||||
|
prev_hunk_staging_operation_count: usize,
|
||||||
cx: &mut Context<Self>,
|
cx: &mut Context<Self>,
|
||||||
) -> oneshot::Receiver<()> {
|
) -> oneshot::Receiver<()> {
|
||||||
match diff_bases_change {
|
match diff_bases_change {
|
||||||
|
@ -2282,12 +2315,13 @@ impl BufferDiffState {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.recalculate_diffs(buffer, cx)
|
self.recalculate_diffs(buffer, prev_hunk_staging_operation_count, cx)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn recalculate_diffs(
|
fn recalculate_diffs(
|
||||||
&mut self,
|
&mut self,
|
||||||
buffer: text::BufferSnapshot,
|
buffer: text::BufferSnapshot,
|
||||||
|
prev_hunk_staging_operation_count: usize,
|
||||||
cx: &mut Context<Self>,
|
cx: &mut Context<Self>,
|
||||||
) -> oneshot::Receiver<()> {
|
) -> oneshot::Receiver<()> {
|
||||||
log::debug!("recalculate diffs");
|
log::debug!("recalculate diffs");
|
||||||
|
@ -2347,6 +2381,12 @@ impl BufferDiffState {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if this.update(cx, |this, _| {
|
||||||
|
this.hunk_staging_operation_count > prev_hunk_staging_operation_count
|
||||||
|
})? {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
let unstaged_changed_range = if let Some((unstaged_diff, new_unstaged_diff)) =
|
let unstaged_changed_range = if let Some((unstaged_diff, new_unstaged_diff)) =
|
||||||
unstaged_diff.as_ref().zip(new_unstaged_diff.clone())
|
unstaged_diff.as_ref().zip(new_unstaged_diff.clone())
|
||||||
{
|
{
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue