| 
 | 1 | +use gix::bstr::{BString, ByteSlice};  | 
 | 2 | +use gix::objs::tree::EntryMode;  | 
 | 3 | +use gix::prelude::ObjectIdExt;  | 
 | 4 | + | 
 | 5 | +pub fn tree(  | 
 | 6 | +    mut repo: gix::Repository,  | 
 | 7 | +    out: &mut dyn std::io::Write,  | 
 | 8 | +    old_treeish: BString,  | 
 | 9 | +    new_treeish: BString,  | 
 | 10 | +) -> anyhow::Result<()> {  | 
 | 11 | +    repo.object_cache_size_if_unset(repo.compute_object_cache_size_for_tree_diffs(&**repo.index_or_empty()?));  | 
 | 12 | + | 
 | 13 | +    let old_tree_id = repo.rev_parse_single(old_treeish.as_bstr())?;  | 
 | 14 | +    let new_tree_id = repo.rev_parse_single(new_treeish.as_bstr())?;  | 
 | 15 | + | 
 | 16 | +    let old_tree = old_tree_id.object()?.peel_to_tree()?;  | 
 | 17 | +    let new_tree = new_tree_id.object()?.peel_to_tree()?;  | 
 | 18 | + | 
 | 19 | +    let changes = repo.diff_tree_to_tree(&old_tree, &new_tree, None)?;  | 
 | 20 | + | 
 | 21 | +    writeln!(  | 
 | 22 | +        out,  | 
 | 23 | +        "Diffing trees `{old_treeish}` ({old_tree_id}) -> `{new_treeish}` ({new_tree_id})\n"  | 
 | 24 | +    )?;  | 
 | 25 | +    write_changes(&repo, out, changes)?;  | 
 | 26 | + | 
 | 27 | +    Ok(())  | 
 | 28 | +}  | 
 | 29 | + | 
 | 30 | +fn write_changes(  | 
 | 31 | +    repo: &gix::Repository,  | 
 | 32 | +    mut out: impl std::io::Write,  | 
 | 33 | +    changes: Vec<gix::diff::tree_with_rewrites::Change>,  | 
 | 34 | +) -> Result<(), std::io::Error> {  | 
 | 35 | +    for change in changes {  | 
 | 36 | +        match change {  | 
 | 37 | +            gix::diff::tree_with_rewrites::Change::Addition {  | 
 | 38 | +                location,  | 
 | 39 | +                id,  | 
 | 40 | +                entry_mode,  | 
 | 41 | +                ..  | 
 | 42 | +            } => {  | 
 | 43 | +                writeln!(out, "A: {}", typed_location(location, entry_mode))?;  | 
 | 44 | +                writeln!(out, "  {}", id.attach(repo).shorten_or_id())?;  | 
 | 45 | +                writeln!(out, "  -> {:o}", entry_mode.0)?;  | 
 | 46 | +            }  | 
 | 47 | +            gix::diff::tree_with_rewrites::Change::Deletion {  | 
 | 48 | +                location,  | 
 | 49 | +                id,  | 
 | 50 | +                entry_mode,  | 
 | 51 | +                ..  | 
 | 52 | +            } => {  | 
 | 53 | +                writeln!(out, "D: {}", typed_location(location, entry_mode))?;  | 
 | 54 | +                writeln!(out, "  {}", id.attach(repo).shorten_or_id())?;  | 
 | 55 | +                writeln!(out, "  {:o} ->", entry_mode.0)?;  | 
 | 56 | +            }  | 
 | 57 | +            gix::diff::tree_with_rewrites::Change::Modification {  | 
 | 58 | +                location,  | 
 | 59 | +                previous_id,  | 
 | 60 | +                id,  | 
 | 61 | +                previous_entry_mode,  | 
 | 62 | +                entry_mode,  | 
 | 63 | +            } => {  | 
 | 64 | +                writeln!(out, "M: {}", typed_location(location, entry_mode))?;  | 
 | 65 | +                writeln!(  | 
 | 66 | +                    out,  | 
 | 67 | +                    "  {previous_id} -> {id}",  | 
 | 68 | +                    previous_id = previous_id.attach(repo).shorten_or_id(),  | 
 | 69 | +                    id = id.attach(repo).shorten_or_id()  | 
 | 70 | +                )?;  | 
 | 71 | +                if previous_entry_mode != entry_mode {  | 
 | 72 | +                    writeln!(out, "  {:o} -> {:o}", previous_entry_mode.0, entry_mode.0)?;  | 
 | 73 | +                }  | 
 | 74 | +            }  | 
 | 75 | +            gix::diff::tree_with_rewrites::Change::Rewrite {  | 
 | 76 | +                source_location,  | 
 | 77 | +                source_id,  | 
 | 78 | +                id,  | 
 | 79 | +                location,  | 
 | 80 | +                source_entry_mode,  | 
 | 81 | +                entry_mode,  | 
 | 82 | +                ..  | 
 | 83 | +            } => {  | 
 | 84 | +                writeln!(  | 
 | 85 | +                    out,  | 
 | 86 | +                    "R: {source} -> {dest}",  | 
 | 87 | +                    source = typed_location(source_location, source_entry_mode),  | 
 | 88 | +                    dest = typed_location(location, entry_mode)  | 
 | 89 | +                )?;  | 
 | 90 | +                writeln!(  | 
 | 91 | +                    out,  | 
 | 92 | +                    "  {source_id} -> {id}",  | 
 | 93 | +                    source_id = source_id.attach(repo).shorten_or_id(),  | 
 | 94 | +                    id = id.attach(repo).shorten_or_id()  | 
 | 95 | +                )?;  | 
 | 96 | +                if source_entry_mode != entry_mode {  | 
 | 97 | +                    writeln!(out, "  {:o} -> {:o}", source_entry_mode.0, entry_mode.0)?;  | 
 | 98 | +                }  | 
 | 99 | +            }  | 
 | 100 | +        };  | 
 | 101 | +    }  | 
 | 102 | + | 
 | 103 | +    Ok(())  | 
 | 104 | +}  | 
 | 105 | + | 
 | 106 | +fn typed_location(mut location: BString, mode: EntryMode) -> BString {  | 
 | 107 | +    if mode.is_tree() {  | 
 | 108 | +        location.push(b'/');  | 
 | 109 | +    }  | 
 | 110 | +    location  | 
 | 111 | +}  | 
0 commit comments