git: Compute and synchronize diffs from HEAD (#23626)
This PR builds on #21258 to make it possible to use HEAD as a diff base. The buffer store is extended to support holding multiple change sets, and collab gains support for synchronizing the committed text of files when any collaborator requires it. Not implemented in this PR: - Exposing the diff from HEAD to the user - Decorating the diff from HEAD with information about which hunks are staged `test_random_multibuffer` now fails first at `SEED=13277`, similar to the previous high-water mark, but with various bugs in the multibuffer logic now shaken out. Release Notes: - N/A --------- Co-authored-by: Max <max@zed.dev> Co-authored-by: Ben <ben@zed.dev> Co-authored-by: Max Brunsfeld <maxbrunsfeld@gmail.com> Co-authored-by: Conrad Irwin <conrad.irwin@gmail.com> Co-authored-by: Conrad <conrad@zed.dev>
This commit is contained in:
parent
871f98bc4d
commit
5704b50fb1
29 changed files with 2799 additions and 603 deletions
|
@ -979,6 +979,7 @@ impl<'a> Iterator for WrapRows<'a> {
|
|||
|
||||
Some(if soft_wrapped {
|
||||
RowInfo {
|
||||
buffer_id: None,
|
||||
buffer_row: None,
|
||||
multibuffer_row: None,
|
||||
diff_status,
|
||||
|
|
|
@ -10137,12 +10137,12 @@ impl Editor {
|
|||
let mut diagnostics;
|
||||
if direction == Direction::Prev {
|
||||
diagnostics = buffer
|
||||
.diagnostics_in_range::<_, usize>(0..search_start)
|
||||
.diagnostics_in_range::<usize>(0..search_start)
|
||||
.collect::<Vec<_>>();
|
||||
diagnostics.reverse();
|
||||
} else {
|
||||
diagnostics = buffer
|
||||
.diagnostics_in_range::<_, usize>(search_start..buffer.len())
|
||||
.diagnostics_in_range::<usize>(search_start..buffer.len())
|
||||
.collect::<Vec<_>>();
|
||||
};
|
||||
let group = diagnostics
|
||||
|
@ -11333,8 +11333,9 @@ impl Editor {
|
|||
if let Some(active_diagnostics) = self.active_diagnostics.as_mut() {
|
||||
let buffer = self.buffer.read(cx).snapshot(cx);
|
||||
let primary_range_start = active_diagnostics.primary_range.start.to_offset(&buffer);
|
||||
let primary_range_end = active_diagnostics.primary_range.end.to_offset(&buffer);
|
||||
let is_valid = buffer
|
||||
.diagnostics_in_range::<_, usize>(active_diagnostics.primary_range.clone())
|
||||
.diagnostics_in_range::<usize>(primary_range_start..primary_range_end)
|
||||
.any(|entry| {
|
||||
entry.diagnostic.is_primary
|
||||
&& !entry.range.is_empty()
|
||||
|
|
|
@ -12431,8 +12431,8 @@ async fn test_multibuffer_reverts(cx: &mut gpui::TestAppContext) {
|
|||
(buffer_2.clone(), base_text_2),
|
||||
(buffer_3.clone(), base_text_3),
|
||||
] {
|
||||
let change_set = cx
|
||||
.new(|cx| BufferChangeSet::new_with_base_text(diff_base.to_string(), &buffer, cx));
|
||||
let change_set =
|
||||
cx.new(|cx| BufferChangeSet::new_with_base_text(&diff_base, &buffer, cx));
|
||||
editor
|
||||
.buffer
|
||||
.update(cx, |buffer, cx| buffer.add_change_set(change_set, cx));
|
||||
|
@ -13125,9 +13125,8 @@ async fn test_toggle_diff_expand_in_multi_buffer(cx: &mut gpui::TestAppContext)
|
|||
(buffer_2.clone(), file_2_old),
|
||||
(buffer_3.clone(), file_3_old),
|
||||
] {
|
||||
let change_set = cx.new(|cx| {
|
||||
BufferChangeSet::new_with_base_text(diff_base.to_string(), &buffer, cx)
|
||||
});
|
||||
let change_set =
|
||||
cx.new(|cx| BufferChangeSet::new_with_base_text(&diff_base, &buffer, cx));
|
||||
editor
|
||||
.buffer
|
||||
.update(cx, |buffer, cx| buffer.add_change_set(change_set, cx));
|
||||
|
@ -13212,7 +13211,7 @@ async fn test_expand_diff_hunk_at_excerpt_boundary(cx: &mut gpui::TestAppContext
|
|||
init_test(cx, |_| {});
|
||||
|
||||
let base = "aaa\nbbb\nccc\nddd\neee\nfff\nggg\n";
|
||||
let text = "aaa\nBBB\nBB2\nccc\nDDD\nEEE\nfff\nggg\n";
|
||||
let text = "aaa\nBBB\nBB2\nccc\nDDD\nEEE\nfff\nggg\nhhh\niii\n";
|
||||
|
||||
let buffer = cx.new(|cx| Buffer::local(text.to_string(), cx));
|
||||
let multi_buffer = cx.new(|cx| {
|
||||
|
@ -13225,7 +13224,11 @@ async fn test_expand_diff_hunk_at_excerpt_boundary(cx: &mut gpui::TestAppContext
|
|||
primary: None,
|
||||
},
|
||||
ExcerptRange {
|
||||
context: Point::new(5, 0)..Point::new(7, 0),
|
||||
context: Point::new(4, 0)..Point::new(7, 0),
|
||||
primary: None,
|
||||
},
|
||||
ExcerptRange {
|
||||
context: Point::new(9, 0)..Point::new(10, 0),
|
||||
primary: None,
|
||||
},
|
||||
],
|
||||
|
@ -13239,8 +13242,7 @@ async fn test_expand_diff_hunk_at_excerpt_boundary(cx: &mut gpui::TestAppContext
|
|||
});
|
||||
editor
|
||||
.update(cx, |editor, _window, cx| {
|
||||
let change_set =
|
||||
cx.new(|cx| BufferChangeSet::new_with_base_text(base.to_string(), &buffer, cx));
|
||||
let change_set = cx.new(|cx| BufferChangeSet::new_with_base_text(base, &buffer, cx));
|
||||
editor
|
||||
.buffer
|
||||
.update(cx, |buffer, cx| buffer.add_change_set(change_set, cx))
|
||||
|
@ -13255,14 +13257,22 @@ async fn test_expand_diff_hunk_at_excerpt_boundary(cx: &mut gpui::TestAppContext
|
|||
});
|
||||
cx.executor().run_until_parked();
|
||||
|
||||
// When the start of a hunk coincides with the start of its excerpt,
|
||||
// the hunk is expanded. When the start of a a hunk is earlier than
|
||||
// the start of its excerpt, the hunk is not expanded.
|
||||
cx.assert_state_with_diff(
|
||||
"
|
||||
ˇaaa
|
||||
- bbb
|
||||
+ BBB
|
||||
|
||||
- ddd
|
||||
- eee
|
||||
+ DDD
|
||||
+ EEE
|
||||
fff
|
||||
|
||||
iii
|
||||
"
|
||||
.unindent(),
|
||||
);
|
||||
|
@ -13500,8 +13510,8 @@ async fn test_toggling_adjacent_diff_hunks(cx: &mut TestAppContext) {
|
|||
|
||||
cx.set_state(indoc! { "
|
||||
one
|
||||
TWO
|
||||
ˇthree
|
||||
ˇTWO
|
||||
three
|
||||
four
|
||||
five
|
||||
"});
|
||||
|
@ -13514,15 +13524,14 @@ async fn test_toggling_adjacent_diff_hunks(cx: &mut TestAppContext) {
|
|||
indoc! { "
|
||||
one
|
||||
- two
|
||||
+ TWO
|
||||
ˇthree
|
||||
+ ˇTWO
|
||||
three
|
||||
four
|
||||
five
|
||||
"}
|
||||
.to_string(),
|
||||
);
|
||||
cx.update_editor(|editor, window, cx| {
|
||||
editor.move_up(&Default::default(), window, cx);
|
||||
editor.move_up(&Default::default(), window, cx);
|
||||
editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
|
||||
});
|
||||
|
@ -14402,12 +14411,8 @@ async fn test_indent_guide_with_expanded_diff_hunks(cx: &mut gpui::TestAppContex
|
|||
|
||||
editor.buffer().update(cx, |multibuffer, cx| {
|
||||
let buffer = multibuffer.as_singleton().unwrap();
|
||||
let change_set = cx.new(|cx| {
|
||||
let mut change_set = BufferChangeSet::new(&buffer, cx);
|
||||
let _ =
|
||||
change_set.set_base_text(base_text.into(), buffer.read(cx).text_snapshot(), cx);
|
||||
change_set
|
||||
});
|
||||
let change_set =
|
||||
cx.new(|cx| BufferChangeSet::new_with_base_text(base_text, &buffer, cx));
|
||||
|
||||
multibuffer.set_all_diff_hunks_expanded(cx);
|
||||
multibuffer.add_change_set(change_set, cx);
|
||||
|
|
|
@ -5295,7 +5295,7 @@ impl EditorElement {
|
|||
if scrollbar_settings.diagnostics != ScrollbarDiagnostics::None {
|
||||
let diagnostics = snapshot
|
||||
.buffer_snapshot
|
||||
.diagnostics_in_range::<_, Point>(Point::zero()..max_point)
|
||||
.diagnostics_in_range::<Point>(Point::zero()..max_point)
|
||||
// Don't show diagnostics the user doesn't care about
|
||||
.filter(|diagnostic| {
|
||||
match (
|
||||
|
|
|
@ -697,7 +697,7 @@ mod tests {
|
|||
fs.set_blame_for_repo(
|
||||
Path::new("/my-repo/.git"),
|
||||
vec![(
|
||||
Path::new("file.txt"),
|
||||
"file.txt".into(),
|
||||
Blame {
|
||||
entries: vec![
|
||||
blame_entry("1b1b1b", 0..1),
|
||||
|
@ -809,7 +809,7 @@ mod tests {
|
|||
fs.set_blame_for_repo(
|
||||
Path::new("/my-repo/.git"),
|
||||
vec![(
|
||||
Path::new("file.txt"),
|
||||
"file.txt".into(),
|
||||
Blame {
|
||||
entries: vec![blame_entry("1b1b1b", 0..4)],
|
||||
..Default::default()
|
||||
|
@ -958,7 +958,7 @@ mod tests {
|
|||
fs.set_blame_for_repo(
|
||||
Path::new("/my-repo/.git"),
|
||||
vec![(
|
||||
Path::new("file.txt"),
|
||||
"file.txt".into(),
|
||||
Blame {
|
||||
entries: blame_entries,
|
||||
..Default::default()
|
||||
|
@ -1000,7 +1000,7 @@ mod tests {
|
|||
fs.set_blame_for_repo(
|
||||
Path::new("/my-repo/.git"),
|
||||
vec![(
|
||||
Path::new("file.txt"),
|
||||
"file.txt".into(),
|
||||
Blame {
|
||||
entries: blame_entries,
|
||||
..Default::default()
|
||||
|
|
1296
crates/editor/src/git/project_diff.rs
Normal file
1296
crates/editor/src/git/project_diff.rs
Normal file
File diff suppressed because it is too large
Load diff
|
@ -279,9 +279,10 @@ fn show_hover(
|
|||
delay.await;
|
||||
}
|
||||
|
||||
let offset = anchor.to_offset(&snapshot.buffer_snapshot);
|
||||
let local_diagnostic = snapshot
|
||||
.buffer_snapshot
|
||||
.diagnostics_in_range::<_, usize>(anchor..anchor)
|
||||
.diagnostics_in_range::<usize>(offset..offset)
|
||||
// Find the entry with the most specific range
|
||||
.min_by_key(|entry| entry.range.len());
|
||||
|
||||
|
|
|
@ -111,11 +111,7 @@ impl ProposedChangesEditor {
|
|||
.read(cx)
|
||||
.change_set_for(buffer.remote_id())?;
|
||||
Some(change_set.update(cx, |change_set, cx| {
|
||||
change_set.set_base_text(
|
||||
base_buffer.read(cx).text(),
|
||||
buffer,
|
||||
cx,
|
||||
)
|
||||
change_set.set_base_text(base_buffer.clone(), buffer, cx)
|
||||
}))
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
|
@ -192,7 +188,7 @@ impl ProposedChangesEditor {
|
|||
new_change_sets.push(cx.new(|cx| {
|
||||
let mut change_set = BufferChangeSet::new(&branch_buffer, cx);
|
||||
let _ = change_set.set_base_text(
|
||||
location.buffer.read(cx).text(),
|
||||
location.buffer.clone(),
|
||||
branch_buffer.read(cx).text_snapshot(),
|
||||
cx,
|
||||
);
|
||||
|
|
|
@ -292,7 +292,7 @@ impl EditorTestContext {
|
|||
let path = self.update_buffer(|buffer, _| buffer.file().unwrap().path().clone());
|
||||
fs.set_index_for_repo(
|
||||
&Self::root_path().join(".git"),
|
||||
&[(path.as_ref(), diff_base.to_string())],
|
||||
&[(path.into(), diff_base.to_string())],
|
||||
);
|
||||
self.cx.run_until_parked();
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue