use gix::bstr::{BString, ByteSlice}; use gix::objs::tree::EntryMode; use gix::odb::store::RefreshMode; use gix::prelude::ObjectIdExt; pub fn tree( mut repo: gix::Repository, out: &mut dyn std::io::Write, old_treeish: BString, new_treeish: BString, ) -> anyhow::Result<()> { repo.object_cache_size_if_unset(repo.compute_object_cache_size_for_tree_diffs(&**repo.index_or_empty()?)); repo.objects.refresh = RefreshMode::Never; let old_tree_id = repo.rev_parse_single(old_treeish.as_bstr())?; let new_tree_id = repo.rev_parse_single(new_treeish.as_bstr())?; let old_tree = old_tree_id.object()?.peel_to_tree()?; let new_tree = new_tree_id.object()?.peel_to_tree()?; let changes = repo.diff_tree_to_tree(&old_tree, &new_tree, None)?; writeln!( out, "Diffing trees `{old_treeish}` ({old_tree_id}) -> `{new_treeish}` ({new_tree_id})\n" )?; write_changes(&repo, out, changes)?; Ok(()) } fn write_changes( repo: &gix::Repository, mut out: impl std::io::Write, changes: Vec, ) -> Result<(), std::io::Error> { for change in changes { match change { gix::diff::tree_with_rewrites::Change::Addition { location, id, entry_mode, .. } => { writeln!(out, "A: {}", typed_location(location, entry_mode))?; writeln!(out, " {}", id.attach(repo).shorten_or_id())?; writeln!(out, " -> {:o}", entry_mode.0)?; } gix::diff::tree_with_rewrites::Change::Deletion { location, id, entry_mode, .. } => { writeln!(out, "D: {}", typed_location(location, entry_mode))?; writeln!(out, " {}", id.attach(repo).shorten_or_id())?; writeln!(out, " {:o} ->", entry_mode.0)?; } gix::diff::tree_with_rewrites::Change::Modification { location, previous_id, id, previous_entry_mode, entry_mode, } => { writeln!(out, "M: {}", typed_location(location, entry_mode))?; writeln!( out, " {previous_id} -> {id}", previous_id = previous_id.attach(repo).shorten_or_id(), id = id.attach(repo).shorten_or_id() )?; if previous_entry_mode != entry_mode { writeln!(out, " {:o} -> {:o}", previous_entry_mode.0, entry_mode.0)?; } } gix::diff::tree_with_rewrites::Change::Rewrite { source_location, source_id, id, location, source_entry_mode, entry_mode, .. } => { writeln!( out, "R: {source} -> {dest}", source = typed_location(source_location, source_entry_mode), dest = typed_location(location, entry_mode) )?; writeln!( out, " {source_id} -> {id}", source_id = source_id.attach(repo).shorten_or_id(), id = id.attach(repo).shorten_or_id() )?; if source_entry_mode != entry_mode { writeln!(out, " {:o} -> {:o}", source_entry_mode.0, entry_mode.0)?; } } }; } Ok(()) } fn typed_location(mut location: BString, mode: EntryMode) -> BString { if mode.is_tree() { location.push(b'/'); } location }