use gix::bstr::ByteSlice; use crate::{ bare::GitBare, cmd::{ diff::{ DiffOptions, DiffStats, diff_tree_to_tree::{matches_pathspec, peel_to_tree}, }, oid::ObjectId, }, errors::{GitError, GitResult}, }; impl GitBare { pub fn diff_stats( &self, old_commit: ObjectId, new_commit: ObjectId, opts: Option, ) -> GitResult { let repo = self.gix_repo()?; let options = opts.unwrap_or_default(); let old_tree_obj = peel_to_tree(&repo, old_commit)?; let new_tree_obj = peel_to_tree(&repo, new_commit)?; let mut platform = old_tree_obj .changes() .map_err(|e| GitError::Gix(e.to_string()))?; platform.options(|opts| { opts.track_rewrites(None); }); let gix_stats = platform .stats(&new_tree_obj) .map_err(|e| GitError::Gix(e.to_string()))?; if options.pathspec.is_empty() { Ok(DiffStats { files_changed: gix_stats.files_changed as usize, insertions: gix_stats.lines_added as usize, deletions: gix_stats.lines_removed as usize, }) } else { let changes = repo .diff_tree_to_tree(&old_tree_obj, &new_tree_obj, None) .map_err(|e| GitError::Gix(e.to_string()))?; let mut resource_cache = repo.diff_resource_cache_for_tree_diff() .map_err(|e| GitError::Gix(e.to_string()))?; let mut stats = DiffStats { files_changed: 0, insertions: 0, deletions: 0, }; for change in &changes { let location = change.location().to_str().unwrap_or(""); if !matches_pathspec(&options.pathspec, location) { continue; } // Skip directories — only diff blobs if change.entry_mode().is_tree() { continue; } resource_cache .set_resource_by_change(change.to_ref(), &repo.objects) .map_err(|e| GitError::Gix(e.to_string()))?; { use gix::diff::blob::platform::prepare_diff::Operation; let prep = resource_cache .prepare_diff() .map_err(|e| GitError::Gix(e.to_string()))?; if let Operation::InternalDiff { algorithm } = prep.operation { let input = prep.interned_input(); let diff = gix::diff::blob::diff_with_slider_heuristics( algorithm, &input, ); stats.files_changed += 1; stats.insertions += diff.count_additions() as usize; stats.deletions += diff.count_removals() as usize; } else if let Operation::SourceOrDestinationIsBinary = prep.operation { stats.files_changed += 1; } } resource_cache.clear_resource_cache_keep_allocation(); } Ok(stats) } } }