content_diff/
content_diff.rs1use std::sync::atomic::AtomicBool;
4
5use girt::content_diff::{BinaryMode, BlobContent, ContentDiff, DiffLimits, diff};
6use girt::{EntryMode, InitKind, ReadLimits, Repository, Tree, TreeEntry};
7
8fn main() -> Result<(), Box<dyn std::error::Error>> {
9 let root = tempfile::tempdir()?;
10 let repo = Repository::init(
11 girt::ObjectFormat::Sha1,
12 root.path().join("repo"),
13 InitKind::Bare,
14 )?;
15
16 let store = repo.loose_objects();
17 let old_blob = store.write_blob(b"heading\r\nold\n")?;
18 let new_blob = store.write_blob(b"heading\r\nnew\xff")?;
19
20 let old = store.write_tree(&Tree::new(
21 girt::ObjectFormat::Sha1,
22 vec![TreeEntry {
23 name: b"example.txt".to_vec(),
24 mode: EntryMode::Blob,
25 id: old_blob,
26 }],
27 )?)?;
28 let new = store.write_tree(&Tree::new(
29 girt::ObjectFormat::Sha1,
30 vec![TreeEntry {
31 name: b"example.txt".to_vec(),
32 mode: EntryMode::Executable,
33 id: new_blob,
34 }],
35 )?)?;
36
37 let objects = repo.objects()?;
38 let cancel = AtomicBool::new(false);
39 let limits = DiffLimits::default();
40
41 let changes = objects.compare_trees(Some(old), Some(new))?;
42 for change in changes {
43 println!(
44 "{}: {:?} -> {:?}",
45 escaped(&change.path),
46 change.old.map(|v| v.mode),
47 change.new.map(|v| v.mode)
48 );
49
50 let content = BlobContent::read(
52 &objects,
53 &change,
54 ReadLimits::default(),
55 limits.max_input_bytes,
56 &cancel,
57 )?;
58
59 match diff(
60 &content.old,
61 &content.new,
62 BinaryMode::Auto,
63 limits,
64 &cancel,
65 )? {
66 ContentDiff::Unchanged => println!(" unchanged content (check modes/existence above)"),
67 ContentDiff::Binary { old, new } => {
68 println!(" binary: {} -> {} bytes", old.len(), new.len())
69 }
70 ContentDiff::Text(text) => {
71 for edit in text.edits() {
72 println!(
73 " old lines {:?} -> new lines {:?} (zero-based)",
74 edit.old_lines, edit.new_lines
75 );
76 println!(" - {}", escaped(&text.old_bytes()[edit.old_bytes.clone()]));
77 println!(" + {}", escaped(&text.new_bytes()[edit.new_bytes.clone()]));
78 }
79 }
80 _ => return Err("unsupported content diff kind".into()),
81 }
82 }
83
84 Ok(())
85}
86
87fn escaped(bytes: &[u8]) -> String {
88 bytes
89 .iter()
90 .flat_map(|b| std::ascii::escape_default(*b))
91 .map(char::from)
92 .collect()
93}