use gitbaby::diff::types::{ ChangedPath, ChangedPathDiff, ChangedPathRequest, DiffOptions, DiffRequest, NumStat, RangeDiffSpec, ShortStat, }; use gix::ObjectId; use serde::{Deserialize, Serialize}; use crate::service::GitService; use crate::share::errors::{GitError, map_baby}; pub fn parse_oid(hex: &str) -> Result { ObjectId::from_hex(hex.trim().as_bytes()) .map_err(|_| GitError::InvalidArgument(format!("invalid object id `{hex}`"))) } #[derive(Debug, Clone, Deserialize, Default)] pub struct DiffOptionsDto { #[serde(default)] pub paths: Option>, #[serde(default)] pub detect_renames: Option, } impl From for DiffOptions { fn from(d: DiffOptionsDto) -> Self { Self { paths: d.paths, detect_renames: d.detect_renames, ..Default::default() } } } #[derive(Debug, Clone, Serialize)] pub struct DiffTreeRecordDto { pub status: String, pub score: u8, pub head_path: Option, pub base_path: Option, pub head_mode: Option, pub base_mode: Option, pub head_blob_id: Option, pub base_blob_id: Option, } impl GitService { async fn diff_request( &self, ctx: &crate::context::facade::GitContext, base: &str, head: &str, ) -> Result { let baby = self.baby(ctx); let left = baby.rev_parse(base).await.map_err(map_baby)?; let right = baby.rev_parse(head).await.map_err(map_baby)?; Ok(DiffRequest { left, right }) } pub async fn diff_raw( &self, space_name: String, repo_name: String, base: String, head: String, ) -> Result { let ctx = self.context(space_name, repo_name).await?; let req = self.diff_request(&ctx, &base, &head).await?; self.baby(&ctx).raw_diff(&req, None).await.map_err(map_baby) } pub async fn diff_files_changed_between( &self, space_name: String, repo_name: String, base: String, head: String, ) -> Result, GitError> { let ctx = self.context(space_name, repo_name).await?; let baby = self.baby(&ctx); let left = baby.rev_parse(&base).await.map_err(map_baby)?; let right = baby.rev_parse(&head).await.map_err(map_baby)?; let paths = baby .find_changed_paths( vec![ChangedPathRequest::TreePair(left, right)], None, None, false, ) .await .map_err(map_baby)?; Ok(paths .into_iter() .map(|p| p.new_path.to_string_lossy().into_owned()) .collect()) } pub async fn diff_short_stat( &self, space_name: String, repo_name: String, base: String, head: String, ) -> Result { let ctx = self.context(space_name, repo_name).await?; let req = self.diff_request(&ctx, &base, &head).await?; self.baby(&ctx) .shortstat(&req, &DiffOptions::default()) .await .map_err(map_baby) } pub async fn diff_stats( &self, space_name: String, repo_name: String, base: String, head: String, opts: DiffOptionsDto, ) -> Result, GitError> { let ctx = self.context(space_name, repo_name).await?; let req = self.diff_request(&ctx, &base, &head).await?; self.baby(&ctx) .diff_stats(&req, &opts.into()) .await .map_err(map_baby) } pub async fn diff_files( &self, space_name: String, repo_name: String, base: String, head: String, opts: DiffOptionsDto, ) -> Result, GitError> { let ctx = self.context(space_name, repo_name).await?; let req = self.diff_request(&ctx, &base, &head).await?; self.baby(&ctx) .commit_diff(&req, &opts.into(), None) .await .map_err(map_baby) } pub async fn diff_for_commit( &self, space_name: String, repo_name: String, commit: String, ) -> Result, GitError> { let ctx = self.context(space_name, repo_name).await?; let oid = parse_oid(&commit)?; self.baby(&ctx) .find_changed_paths(vec![ChangedPathRequest::Commit(oid)], None, None, false) .await .map_err(map_baby) } pub async fn diff_tree( &self, space_name: String, repo_name: String, base: String, head: String, use_merge_base: bool, ) -> Result, GitError> { let ctx = self.context(space_name, repo_name).await?; let baby = self.baby(&ctx); let left = if use_merge_base { let mb = baby.merge_base(&base, &head).await.map_err(map_baby)?; mb.to_string() } else { baby.rev_parse(&base).await.map_err(map_baby)?.to_string() }; let right = baby.rev_parse(&head).await.map_err(map_baby)?; let rows = baby .diff_trees( &left, &right.to_string(), std::path::Path::new(""), gitbaby::tree::types::DiffTreesOptions { recurse: true, detect_renames: true, ..Default::default() }, ) .await .map_err(map_baby)?; Ok(rows .into_iter() .map(|d| DiffTreeRecordDto { status: format!("{:?}", d.status).to_lowercase(), score: 0, head_path: d.new_path.map(|p| p.to_string_lossy().into_owned()), base_path: d.old_path.map(|p| p.to_string_lossy().into_owned()), head_mode: d.new_mode.map(|m| m as u32), base_mode: d.old_mode.map(|m| m as u32), head_blob_id: d.new_oid.map(|o| o.to_string()), base_blob_id: d.old_oid.map(|o| o.to_string()), }) .collect()) } pub async fn range_diff( &self, space_name: String, repo_name: String, spec: RangeDiffSpec, ) -> Result { let ctx = self.context(space_name, repo_name).await?; self.baby(&ctx) .range_diff(&spec, &DiffOptions::default(), None) .await .map_err(map_baby) } }