From 6dc68e02a72527ed7af585b03a873dfcc4d41b65 Mon Sep 17 00:00:00 2001 From: Conrad Irwin Date: Wed, 3 Sep 2025 15:42:10 -0700 Subject: [PATCH] TEMP --- crates/git/src/repository.rs | 105 ++++++++++++++++++++++++++++++++ crates/git_ui/src/git_ui.rs | 1 + crates/project/src/git_store.rs | 32 ++++++++++ 3 files changed, 138 insertions(+) diff --git a/crates/git/src/repository.rs b/crates/git/src/repository.rs index fd12dafa98575b5d8b0190d6ed4e894ac61e8165..7d2509d9c86fa27ca4a8eee993d0e32ba690198e 100644 --- a/crates/git/src/repository.rs +++ b/crates/git/src/repository.rs @@ -309,6 +309,8 @@ pub trait GitRepository: Send + Sync { /// Also returns `None` for symlinks. fn load_committed_text(&self, path: RepoPath) -> BoxFuture<'_, Option>; + fn merge_base(&self, commits: Vec) + fn set_index_text( &self, path: RepoPath, @@ -360,6 +362,7 @@ pub trait GitRepository: Send + Sync { fn show(&self, commit: String) -> BoxFuture<'_, Result>; fn load_commit(&self, commit: String, cx: AsyncApp) -> BoxFuture<'_, Result>; + fn diff_to_commit(&self, commit: String, cx: AsyncApp) -> BoxFuture<'_, Result>; fn blame(&self, path: RepoPath, content: Rope) -> BoxFuture<'_, Result>; /// Returns the absolute path to the repository. For worktrees, this will be the path to the @@ -614,6 +617,108 @@ impl GitRepository for RealGitRepository { .boxed() } + fn diff_to_commit(&self, commit: String, cx: AsyncApp) -> BoxFuture<'_, Result> { + let Some(working_directory) = self.repository.lock().workdir().map(ToOwned::to_owned) + else { + return future::ready(Err(anyhow!("no working directory"))).boxed(); + }; + cx.background_spawn(async move { + let show_output = util::command::new_std_command("git") + .current_dir(&working_directory) + .args([ + "--no-optional-locks", + "diff", + "--format=%P", + "-z", + "--no-renames", + "--name-status", + ]) + .arg(&commit) + .stdin(Stdio::null()) + .stdout(Stdio::piped()) + .stderr(Stdio::piped()) + .output() + .context("starting git show process")?; + + let show_stdout = String::from_utf8_lossy(&show_output.stdout); + let mut lines = show_stdout.split('\n'); + let parent_sha = lines.next().unwrap().trim().trim_end_matches('\0'); + let changes = parse_git_diff_name_status(lines.next().unwrap_or("")); + + let mut cat_file_process = util::command::new_std_command("git") + .current_dir(&working_directory) + .args(["--no-optional-locks", "cat-file", "--batch=%(objectsize)"]) + .stdin(Stdio::piped()) + .stdout(Stdio::piped()) + .stderr(Stdio::piped()) + .spawn() + .context("starting git cat-file process")?; + + use std::io::Write as _; + let mut files = Vec::::new(); + let mut stdin = BufWriter::with_capacity(512, cat_file_process.stdin.take().unwrap()); + let mut stdout = BufReader::new(cat_file_process.stdout.take().unwrap()); + let mut info_line = String::new(); + let mut newline = [b'\0']; + for (path, status_code) in changes { + match status_code { + StatusCode::Modified => { + // writeln!(&mut stdin, "{commit}:{}", path.display())?; + writeln!(&mut stdin, "{parent_sha}:{}", path.display())?; + } + StatusCode::Added => { + // writeln!(&mut stdin, "{commit}:{}", path.display())?; + } + StatusCode::Deleted => { + writeln!(&mut stdin, "{parent_sha}:{}", path.display())?; + } + _ => continue, + } + stdin.flush()?; + + info_line.clear(); + stdout.read_line(&mut info_line)?; + + let len = info_line.trim_end().parse().with_context(|| { + format!("invalid object size output from cat-file {info_line}") + })?; + let mut text = vec![0; len]; + stdout.read_exact(&mut text)?; + stdout.read_exact(&mut newline)?; + let text = String::from_utf8_lossy(&text).to_string(); + + let mut old_text = None; + // let mut new_text = None; + match status_code { + StatusCode::Modified => { + info_line.clear(); + stdout.read_line(&mut info_line)?; + let len = info_line.trim_end().parse().with_context(|| { + format!("invalid object size output from cat-file {}", info_line) + })?; + let mut parent_text = vec![0; len]; + stdout.read_exact(&mut parent_text)?; + stdout.read_exact(&mut newline)?; + old_text = Some(String::from_utf8_lossy(&parent_text).to_string()); + // new_text = Some(text); + } + // StatusCode::Added => new_text = Some(text), + StatusCode::Deleted => old_text = Some(text), + _ => continue, + } + + files.push(CommitFile { + path: path.into(), + old_text, + new_text: None, + }) + } + + Ok(CommitDiff { files }) + }) + .boxed() + } + fn load_commit(&self, commit: String, cx: AsyncApp) -> BoxFuture<'_, Result> { let Some(working_directory) = self.repository.lock().workdir().map(ToOwned::to_owned) else { diff --git a/crates/git_ui/src/git_ui.rs b/crates/git_ui/src/git_ui.rs index 5369b8b404ba7005bf738f4515d20b3fe4163a48..52fcf93f4d39039cc5c847141038ffb22ea38b26 100644 --- a/crates/git_ui/src/git_ui.rs +++ b/crates/git_ui/src/git_ui.rs @@ -23,6 +23,7 @@ use zed_actions; use crate::{git_panel::GitPanel, text_diff_view::TextDiffView}; mod askpass_modal; +pub mod branch_diff; pub mod branch_picker; mod commit_modal; pub mod commit_tooltip; diff --git a/crates/project/src/git_store.rs b/crates/project/src/git_store.rs index b7ff3e7fefc9b2e8aede04d3cd0fca88c16c2a62..8d19f65a0d96fe1bd129fdfe5711e5de33f173d6 100644 --- a/crates/project/src/git_store.rs +++ b/crates/project/src/git_store.rs @@ -3467,6 +3467,38 @@ impl Repository { }) } + pub fn diff_to_commit(&mut self, commit: String) -> oneshot::Receiver> { + let id = self.id; + self.send_job(None, move |git_repo, cx| async move { + match git_repo { + RepositoryState::Local { backend, .. } => backend.diff_to_commit(commit, cx).await, + RepositoryState::Remote { + client, project_id, .. + } => { + todo!(); + let response = client + .request(proto::LoadCommitDiff { + project_id: project_id.0, + repository_id: id.to_proto(), + commit, + }) + .await?; + Ok(CommitDiff { + files: response + .files + .into_iter() + .map(|file| CommitFile { + path: Path::new(&file.path).into(), + old_text: file.old_text, + new_text: file.new_text, + }) + .collect(), + }) + } + } + }) + } + fn buffer_store(&self, cx: &App) -> Option> { Some(self.git_store.upgrade()?.read(cx).buffer_store.clone()) }