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