use crate::bare::GitBare; use crate::error::{GitError, GitResult}; use crate::pb::{GetCommitDiffRequest, GetDiffRequest, GetDiffResponse}; impl GitBare { pub fn get_commit_diff(&self, request: GetCommitDiffRequest) -> GitResult { let commit = match request.commit.and_then(|s| s.selector) { Some(crate::pb::object_selector::Selector::Oid(oid)) => oid.hex, Some(crate::pb::object_selector::Selector::Revision(name)) => name.revision, None => "HEAD".into(), }; let base = self.first_parent_or_empty_tree(&commit)?; self.get_diff(GetDiffRequest { repository: request.repository, base: Some(crate::pb::ObjectSelector { selector: Some(crate::pb::object_selector::Selector::Revision( crate::pb::ObjectName { revision: base }, )), }), head: Some(crate::pb::ObjectSelector { selector: Some(crate::pb::object_selector::Selector::Revision( crate::pb::ObjectName { revision: commit }, )), }), options: request.options, pagination: request.pagination, }) } fn first_parent_or_empty_tree(&self, commit: &str) -> GitResult { let result = duct::cmd( "git", [ "--git-dir", self.bare_dir.to_string_lossy().as_ref(), "rev-list", "--parents", "-n", "1", commit, ], ) .stdout_capture() .stderr_capture() .unchecked() .run()?; if !result.status.success() { return Err(GitError::CommandFailed { status_code: result.status.code(), stderr: String::from_utf8_lossy(&result.stderr).into_owned(), }); } let output = String::from_utf8_lossy(&result.stdout); let parts = output.split_whitespace().collect::>(); if let Some(parent) = parts.get(1) { return Ok((*parent).to_string()); } let empty_tree = duct::cmd( "git", [ "--git-dir", self.bare_dir.to_string_lossy().as_ref(), "hash-object", "-t", "tree", "-w", "--stdin", ], ) .stdin_bytes(Vec::::new()) .stdout_capture() .stderr_capture() .unchecked() .run()?; if !empty_tree.status.success() { return Err(GitError::CommandFailed { status_code: empty_tree.status.code(), stderr: String::from_utf8_lossy(&empty_tree.stderr).into_owned(), }); } Ok(String::from_utf8_lossy(&empty_tree.stdout) .trim() .to_string()) } }