Restructure git diff state management to allow viewing buffers with different diff bases (#21258)

This is a pure refactor of our Git diff state management. Buffers are no
longer are associated with one single diff (the unstaged changes).
Instead, there is an explicit project API for retrieving a buffer's
unstaged changes, and the `Editor` view layer is responsible for
choosing what diff to associate with a buffer.

The reason for this change is that we'll soon want to add multiple "git
diff views" to Zed, one of which will show the *uncommitted* changes for
a buffer. But that view will need to co-exist with other views of the
same buffer, which may want to show the unstaged changes.

### Todo

* [x] Get git gutter and git hunks working with new structure
* [x] Update editor tests to use new APIs
* [x] Update buffer tests
* [x] Restructure remoting/collab protocol
* [x] Update assertions about staged text in
`random_project_collaboration_tests`
* [x] Move buffer tests for git diff management to a new spot, using the
new APIs

Release Notes:

- N/A

---------

Co-authored-by: Richard <richard@zed.dev>
Co-authored-by: Cole <cole@zed.dev>
Co-authored-by: Conrad <conrad@zed.dev>
This commit is contained in:
Max Brunsfeld 2024-12-04 15:02:33 -08:00 committed by GitHub
parent 31796171de
commit a2115e7242
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
29 changed files with 1832 additions and 1651 deletions

View file

@ -104,7 +104,6 @@ pub enum CreatedEntry {
pub struct LoadedFile {
pub file: Arc<File>,
pub text: String,
pub diff_base: Option<String>,
}
pub struct LoadedBinaryFile {
@ -707,6 +706,30 @@ impl Worktree {
}
}
pub fn load_staged_file(&self, path: &Path, cx: &AppContext) -> Task<Result<Option<String>>> {
match self {
Worktree::Local(this) => {
let path = Arc::from(path);
let snapshot = this.snapshot();
cx.background_executor().spawn(async move {
if let Some(repo) = snapshot.repository_for_path(&path) {
if let Some(repo_path) = repo.relativize(&snapshot, &path).log_err() {
if let Some(git_repo) =
snapshot.git_repositories.get(&*repo.work_directory)
{
return Ok(git_repo.repo_ptr.load_index_text(&repo_path));
}
}
}
Ok(None)
})
}
Worktree::Remote(_) => {
Task::ready(Err(anyhow!("remote worktrees can't yet load staged files")))
}
}
}
pub fn load_binary_file(
&self,
path: &Path,
@ -1362,28 +1385,9 @@ impl LocalWorktree {
let entry = self.refresh_entry(path.clone(), None, cx);
let is_private = self.is_path_private(path.as_ref());
cx.spawn(|this, mut cx| async move {
cx.spawn(|this, _cx| async move {
let abs_path = abs_path?;
let text = fs.load(&abs_path).await?;
let mut index_task = None;
let snapshot = this.update(&mut cx, |this, _| this.as_local().unwrap().snapshot())?;
if let Some(repo) = snapshot.repository_for_path(&path) {
if let Some(repo_path) = repo.relativize(&snapshot, &path).log_err() {
if let Some(git_repo) = snapshot.git_repositories.get(&*repo.work_directory) {
let git_repo = git_repo.repo_ptr.clone();
index_task = Some(
cx.background_executor()
.spawn(async move { git_repo.load_index_text(&repo_path) }),
);
}
}
}
let diff_base = if let Some(index_task) = index_task {
index_task.await
} else {
None
};
let worktree = this
.upgrade()
@ -1413,11 +1417,7 @@ impl LocalWorktree {
}
};
Ok(LoadedFile {
file,
text,
diff_base,
})
Ok(LoadedFile { file, text })
})
}