gitdataai/lib/git/cmd/diff/diff_stats.rs
zhenyi 779e4eae2f feat(channel): add article feed and composer with room type support
- Add ArticleFeed component for article-based channels
- Implement ArticleComposer with draft persistence
- Add Newspaper icon for article room type
- Update ChannelPage to conditionally render article feed vs message view
- Add article-related API endpoints and models
- Reset thread view when switching rooms
- Add room type check in channel sidebar
- Update CSS to hide scrollbars globally
- Add gRPC message size limit configuration
- Fix git diff tree handling
2026-05-31 03:09:49 +08:00

104 lines
3.4 KiB
Rust

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<DiffOptions>,
) -> GitResult<DiffStats> {
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)
}
}
}