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
|
@ -2558,13 +2558,27 @@ async fn test_git_diff_base_change(
|
|||
|
||||
let project_remote = client_b.join_remote_project(project_id, cx_b).await;
|
||||
|
||||
let diff_base = "
|
||||
let staged_text = "
|
||||
one
|
||||
three
|
||||
"
|
||||
.unindent();
|
||||
|
||||
let new_diff_base = "
|
||||
let committed_text = "
|
||||
one
|
||||
TWO
|
||||
three
|
||||
"
|
||||
.unindent();
|
||||
|
||||
let new_committed_text = "
|
||||
one
|
||||
TWO_HUNDRED
|
||||
three
|
||||
"
|
||||
.unindent();
|
||||
|
||||
let new_staged_text = "
|
||||
one
|
||||
two
|
||||
"
|
||||
|
@ -2572,7 +2586,11 @@ async fn test_git_diff_base_change(
|
|||
|
||||
client_a.fs().set_index_for_repo(
|
||||
Path::new("/dir/.git"),
|
||||
&[(Path::new("a.txt"), diff_base.clone())],
|
||||
&[("a.txt".into(), staged_text.clone())],
|
||||
);
|
||||
client_a.fs().set_head_for_repo(
|
||||
Path::new("/dir/.git"),
|
||||
&[("a.txt".into(), committed_text.clone())],
|
||||
);
|
||||
|
||||
// Create the buffer
|
||||
|
@ -2580,7 +2598,7 @@ async fn test_git_diff_base_change(
|
|||
.update(cx_a, |p, cx| p.open_buffer((worktree_id, "a.txt"), cx))
|
||||
.await
|
||||
.unwrap();
|
||||
let change_set_local_a = project_local
|
||||
let local_unstaged_changes_a = project_local
|
||||
.update(cx_a, |p, cx| {
|
||||
p.open_unstaged_changes(buffer_local_a.clone(), cx)
|
||||
})
|
||||
|
@ -2589,16 +2607,16 @@ async fn test_git_diff_base_change(
|
|||
|
||||
// Wait for it to catch up to the new diff
|
||||
executor.run_until_parked();
|
||||
change_set_local_a.read_with(cx_a, |change_set, cx| {
|
||||
local_unstaged_changes_a.read_with(cx_a, |change_set, cx| {
|
||||
let buffer = buffer_local_a.read(cx);
|
||||
assert_eq!(
|
||||
change_set.base_text_string().as_deref(),
|
||||
Some(diff_base.as_str())
|
||||
Some(staged_text.as_str())
|
||||
);
|
||||
git::diff::assert_hunks(
|
||||
change_set.diff_to_buffer.hunks_in_row_range(0..4, buffer),
|
||||
buffer,
|
||||
&diff_base,
|
||||
&change_set.base_text_string().unwrap(),
|
||||
&[(1..2, "", "two\n")],
|
||||
);
|
||||
});
|
||||
|
@ -2608,7 +2626,7 @@ async fn test_git_diff_base_change(
|
|||
.update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.txt"), cx))
|
||||
.await
|
||||
.unwrap();
|
||||
let change_set_remote_a = project_remote
|
||||
let remote_unstaged_changes_a = project_remote
|
||||
.update(cx_b, |p, cx| {
|
||||
p.open_unstaged_changes(buffer_remote_a.clone(), cx)
|
||||
})
|
||||
|
@ -2617,64 +2635,104 @@ async fn test_git_diff_base_change(
|
|||
|
||||
// Wait remote buffer to catch up to the new diff
|
||||
executor.run_until_parked();
|
||||
change_set_remote_a.read_with(cx_b, |change_set, cx| {
|
||||
remote_unstaged_changes_a.read_with(cx_b, |change_set, cx| {
|
||||
let buffer = buffer_remote_a.read(cx);
|
||||
assert_eq!(
|
||||
change_set.base_text_string().as_deref(),
|
||||
Some(diff_base.as_str())
|
||||
Some(staged_text.as_str())
|
||||
);
|
||||
git::diff::assert_hunks(
|
||||
change_set.diff_to_buffer.hunks_in_row_range(0..4, buffer),
|
||||
buffer,
|
||||
&diff_base,
|
||||
&change_set.base_text_string().unwrap(),
|
||||
&[(1..2, "", "two\n")],
|
||||
);
|
||||
});
|
||||
|
||||
// Update the staged text of the open buffer
|
||||
// Open uncommitted changes on the guest, without opening them on the host first
|
||||
let remote_uncommitted_changes_a = project_remote
|
||||
.update(cx_b, |p, cx| {
|
||||
p.open_uncommitted_changes(buffer_remote_a.clone(), cx)
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
executor.run_until_parked();
|
||||
remote_uncommitted_changes_a.read_with(cx_b, |change_set, cx| {
|
||||
let buffer = buffer_remote_a.read(cx);
|
||||
assert_eq!(
|
||||
change_set.base_text_string().as_deref(),
|
||||
Some(committed_text.as_str())
|
||||
);
|
||||
git::diff::assert_hunks(
|
||||
change_set.diff_to_buffer.hunks_in_row_range(0..4, buffer),
|
||||
buffer,
|
||||
&change_set.base_text_string().unwrap(),
|
||||
&[(1..2, "TWO\n", "two\n")],
|
||||
);
|
||||
});
|
||||
|
||||
// Update the index text of the open buffer
|
||||
client_a.fs().set_index_for_repo(
|
||||
Path::new("/dir/.git"),
|
||||
&[(Path::new("a.txt"), new_diff_base.clone())],
|
||||
&[("a.txt".into(), new_staged_text.clone())],
|
||||
);
|
||||
client_a.fs().set_head_for_repo(
|
||||
Path::new("/dir/.git"),
|
||||
&[("a.txt".into(), new_committed_text.clone())],
|
||||
);
|
||||
|
||||
// Wait for buffer_local_a to receive it
|
||||
executor.run_until_parked();
|
||||
change_set_local_a.read_with(cx_a, |change_set, cx| {
|
||||
local_unstaged_changes_a.read_with(cx_a, |change_set, cx| {
|
||||
let buffer = buffer_local_a.read(cx);
|
||||
assert_eq!(
|
||||
change_set.base_text_string().as_deref(),
|
||||
Some(new_diff_base.as_str())
|
||||
Some(new_staged_text.as_str())
|
||||
);
|
||||
git::diff::assert_hunks(
|
||||
change_set.diff_to_buffer.hunks_in_row_range(0..4, buffer),
|
||||
buffer,
|
||||
&new_diff_base,
|
||||
&change_set.base_text_string().unwrap(),
|
||||
&[(2..3, "", "three\n")],
|
||||
);
|
||||
});
|
||||
|
||||
change_set_remote_a.read_with(cx_b, |change_set, cx| {
|
||||
remote_unstaged_changes_a.read_with(cx_b, |change_set, cx| {
|
||||
let buffer = buffer_remote_a.read(cx);
|
||||
assert_eq!(
|
||||
change_set.base_text_string().as_deref(),
|
||||
Some(new_diff_base.as_str())
|
||||
Some(new_staged_text.as_str())
|
||||
);
|
||||
git::diff::assert_hunks(
|
||||
change_set.diff_to_buffer.hunks_in_row_range(0..4, buffer),
|
||||
buffer,
|
||||
&new_diff_base,
|
||||
&change_set.base_text_string().unwrap(),
|
||||
&[(2..3, "", "three\n")],
|
||||
);
|
||||
});
|
||||
|
||||
remote_uncommitted_changes_a.read_with(cx_b, |change_set, cx| {
|
||||
let buffer = buffer_remote_a.read(cx);
|
||||
assert_eq!(
|
||||
change_set.base_text_string().as_deref(),
|
||||
Some(new_committed_text.as_str())
|
||||
);
|
||||
git::diff::assert_hunks(
|
||||
change_set.diff_to_buffer.hunks_in_row_range(0..4, buffer),
|
||||
buffer,
|
||||
&change_set.base_text_string().unwrap(),
|
||||
&[(1..2, "TWO_HUNDRED\n", "two\n")],
|
||||
);
|
||||
});
|
||||
|
||||
// Nested git dir
|
||||
let diff_base = "
|
||||
let staged_text = "
|
||||
one
|
||||
three
|
||||
"
|
||||
.unindent();
|
||||
|
||||
let new_diff_base = "
|
||||
let new_staged_text = "
|
||||
one
|
||||
two
|
||||
"
|
||||
|
@ -2682,7 +2740,7 @@ async fn test_git_diff_base_change(
|
|||
|
||||
client_a.fs().set_index_for_repo(
|
||||
Path::new("/dir/sub/.git"),
|
||||
&[(Path::new("b.txt"), diff_base.clone())],
|
||||
&[("b.txt".into(), staged_text.clone())],
|
||||
);
|
||||
|
||||
// Create the buffer
|
||||
|
@ -2690,7 +2748,7 @@ async fn test_git_diff_base_change(
|
|||
.update(cx_a, |p, cx| p.open_buffer((worktree_id, "sub/b.txt"), cx))
|
||||
.await
|
||||
.unwrap();
|
||||
let change_set_local_b = project_local
|
||||
let local_unstaged_changes_b = project_local
|
||||
.update(cx_a, |p, cx| {
|
||||
p.open_unstaged_changes(buffer_local_b.clone(), cx)
|
||||
})
|
||||
|
@ -2699,16 +2757,16 @@ async fn test_git_diff_base_change(
|
|||
|
||||
// Wait for it to catch up to the new diff
|
||||
executor.run_until_parked();
|
||||
change_set_local_b.read_with(cx_a, |change_set, cx| {
|
||||
local_unstaged_changes_b.read_with(cx_a, |change_set, cx| {
|
||||
let buffer = buffer_local_b.read(cx);
|
||||
assert_eq!(
|
||||
change_set.base_text_string().as_deref(),
|
||||
Some(diff_base.as_str())
|
||||
Some(staged_text.as_str())
|
||||
);
|
||||
git::diff::assert_hunks(
|
||||
change_set.diff_to_buffer.hunks_in_row_range(0..4, buffer),
|
||||
buffer,
|
||||
&diff_base,
|
||||
&change_set.base_text_string().unwrap(),
|
||||
&[(1..2, "", "two\n")],
|
||||
);
|
||||
});
|
||||
|
@ -2718,7 +2776,7 @@ async fn test_git_diff_base_change(
|
|||
.update(cx_b, |p, cx| p.open_buffer((worktree_id, "sub/b.txt"), cx))
|
||||
.await
|
||||
.unwrap();
|
||||
let change_set_remote_b = project_remote
|
||||
let remote_unstaged_changes_b = project_remote
|
||||
.update(cx_b, |p, cx| {
|
||||
p.open_unstaged_changes(buffer_remote_b.clone(), cx)
|
||||
})
|
||||
|
@ -2726,52 +2784,52 @@ async fn test_git_diff_base_change(
|
|||
.unwrap();
|
||||
|
||||
executor.run_until_parked();
|
||||
change_set_remote_b.read_with(cx_b, |change_set, cx| {
|
||||
remote_unstaged_changes_b.read_with(cx_b, |change_set, cx| {
|
||||
let buffer = buffer_remote_b.read(cx);
|
||||
assert_eq!(
|
||||
change_set.base_text_string().as_deref(),
|
||||
Some(diff_base.as_str())
|
||||
Some(staged_text.as_str())
|
||||
);
|
||||
git::diff::assert_hunks(
|
||||
change_set.diff_to_buffer.hunks_in_row_range(0..4, buffer),
|
||||
buffer,
|
||||
&diff_base,
|
||||
&staged_text,
|
||||
&[(1..2, "", "two\n")],
|
||||
);
|
||||
});
|
||||
|
||||
// Update the staged text
|
||||
// Updatet the staged text
|
||||
client_a.fs().set_index_for_repo(
|
||||
Path::new("/dir/sub/.git"),
|
||||
&[(Path::new("b.txt"), new_diff_base.clone())],
|
||||
&[("b.txt".into(), new_staged_text.clone())],
|
||||
);
|
||||
|
||||
// Wait for buffer_local_b to receive it
|
||||
executor.run_until_parked();
|
||||
change_set_local_b.read_with(cx_a, |change_set, cx| {
|
||||
local_unstaged_changes_b.read_with(cx_a, |change_set, cx| {
|
||||
let buffer = buffer_local_b.read(cx);
|
||||
assert_eq!(
|
||||
change_set.base_text_string().as_deref(),
|
||||
Some(new_diff_base.as_str())
|
||||
Some(new_staged_text.as_str())
|
||||
);
|
||||
git::diff::assert_hunks(
|
||||
change_set.diff_to_buffer.hunks_in_row_range(0..4, buffer),
|
||||
buffer,
|
||||
&new_diff_base,
|
||||
&new_staged_text,
|
||||
&[(2..3, "", "three\n")],
|
||||
);
|
||||
});
|
||||
|
||||
change_set_remote_b.read_with(cx_b, |change_set, cx| {
|
||||
remote_unstaged_changes_b.read_with(cx_b, |change_set, cx| {
|
||||
let buffer = buffer_remote_b.read(cx);
|
||||
assert_eq!(
|
||||
change_set.base_text_string().as_deref(),
|
||||
Some(new_diff_base.as_str())
|
||||
Some(new_staged_text.as_str())
|
||||
);
|
||||
git::diff::assert_hunks(
|
||||
change_set.diff_to_buffer.hunks_in_row_range(0..4, buffer),
|
||||
buffer,
|
||||
&new_diff_base,
|
||||
&new_staged_text,
|
||||
&[(2..3, "", "three\n")],
|
||||
);
|
||||
});
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue