153 lines
5.3 KiB
Rust
153 lines
5.3 KiB
Rust
use gix::bstr::ByteSlice;
|
|
|
|
use crate::{
|
|
bare::GitBare,
|
|
cmd::{
|
|
diff::{
|
|
DiffOptions, DiffResult, DiffStats,
|
|
diff_tree_to_tree::{
|
|
HunkCollector, change_to_delta, matches_pathspec,
|
|
},
|
|
},
|
|
oid::ObjectId,
|
|
},
|
|
errors::GitResult,
|
|
};
|
|
|
|
impl GitBare {
|
|
pub fn diff_index_to_tree(
|
|
&self,
|
|
tree: ObjectId,
|
|
opts: Option<DiffOptions>,
|
|
) -> GitResult<DiffResult> {
|
|
let options = opts.unwrap_or_default();
|
|
let repo = self.gix_repo()?;
|
|
let gix_id: gix::hash::ObjectId = (&tree).try_into()?;
|
|
let tree_obj = if let Ok(tree) = repo.find_tree(gix_id) {
|
|
tree
|
|
} else {
|
|
let commit = repo
|
|
.find_commit(gix_id)
|
|
.map_err(|e| crate::errors::GitError::Gix(e.to_string()))?;
|
|
let tree_id = commit
|
|
.tree_id()
|
|
.map_err(|e| crate::errors::GitError::Gix(e.to_string()))?
|
|
.detach();
|
|
repo.find_tree(tree_id)
|
|
.map_err(|e| crate::errors::GitError::Gix(e.to_string()))?
|
|
};
|
|
let head_id = repo
|
|
.head_id()
|
|
.map_err(|e| crate::errors::GitError::Gix(e.to_string()))?;
|
|
let head_commit = repo
|
|
.find_commit(head_id.detach())
|
|
.map_err(|e| crate::errors::GitError::Gix(e.to_string()))?;
|
|
let head_tree_id = head_commit
|
|
.tree_id()
|
|
.map_err(|e| crate::errors::GitError::Gix(e.to_string()))?
|
|
.detach();
|
|
let head_tree = repo
|
|
.find_tree(head_tree_id)
|
|
.map_err(|e| crate::errors::GitError::Gix(e.to_string()))?;
|
|
let tree_oid = tree_obj.id().detach();
|
|
let head_tree_oid = head_tree.id().detach();
|
|
if tree_oid == head_tree_oid {
|
|
return Ok(DiffResult {
|
|
stats: DiffStats {
|
|
files_changed: 0,
|
|
insertions: 0,
|
|
deletions: 0,
|
|
},
|
|
deltas: Vec::new(),
|
|
});
|
|
}
|
|
|
|
let mut diff_opts = gix::diff::Options::default();
|
|
diff_opts.track_path();
|
|
|
|
let changes = repo
|
|
.diff_tree_to_tree(&tree_obj, &head_tree, Some(diff_opts))
|
|
.map_err(|e| crate::errors::GitError::Gix(e.to_string()))?;
|
|
|
|
let mut resource_cache = repo
|
|
.diff_resource_cache_for_tree_diff()
|
|
.map_err(|e| crate::errors::GitError::Gix(e.to_string()))?;
|
|
|
|
let mut deltas = Vec::new();
|
|
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;
|
|
}
|
|
|
|
let mut delta = change_to_delta(change);
|
|
|
|
resource_cache
|
|
.set_resource_by_change(change.to_ref(), &repo.objects)
|
|
.map_err(|e| crate::errors::GitError::Gix(e.to_string()))?;
|
|
|
|
let is_binary = {
|
|
use gix::diff::blob::platform::prepare_diff::Operation;
|
|
|
|
let prep = resource_cache
|
|
.prepare_diff()
|
|
.map_err(|e| crate::errors::GitError::Gix(e.to_string()))?;
|
|
|
|
match prep.operation {
|
|
Operation::InternalDiff { algorithm } => {
|
|
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;
|
|
|
|
if options.context_lines > 0 {
|
|
let ctx = gix::diff::blob::unified_diff::ContextSize::symmetrical(
|
|
options.context_lines.max(3),
|
|
);
|
|
let collector = HunkCollector::new();
|
|
let unified = gix::diff::blob::UnifiedDiff::new(
|
|
&diff, &input, collector, ctx,
|
|
);
|
|
let (hunks, lines) =
|
|
unified.consume().map_err(|e| {
|
|
crate::errors::GitError::Gix(e.to_string())
|
|
})?;
|
|
delta.hunks = hunks;
|
|
delta.lines = lines;
|
|
}
|
|
false
|
|
}
|
|
Operation::SourceOrDestinationIsBinary => {
|
|
stats.files_changed += 1;
|
|
true
|
|
}
|
|
Operation::ExternalCommand { .. } => {
|
|
stats.files_changed += 1;
|
|
false
|
|
}
|
|
}
|
|
};
|
|
|
|
if is_binary {
|
|
delta.old_file.is_binary = true;
|
|
delta.new_file.is_binary = true;
|
|
}
|
|
|
|
resource_cache.clear_resource_cache_keep_allocation();
|
|
deltas.push(delta);
|
|
}
|
|
|
|
Ok(DiffResult { stats, deltas })
|
|
}
|
|
}
|