compare_trees/
compare_trees.rs1use std::path::PathBuf;
4use std::str::FromStr;
5
6use clap::Parser;
7use girt::display::quote_path;
8use girt::{EntryMode, InitKind, ObjectId, Repository, Tree, TreeEntry, TreeValue};
9
10#[derive(Clone, Copy, Debug)]
12struct Side(Option<ObjectId>);
13
14impl FromStr for Side {
15 type Err = girt::ParseObjectIdError;
16
17 fn from_str(text: &str) -> Result<Self, Self::Err> {
18 if text == "-" {
19 return Ok(Self(None));
20 }
21 let id = text.parse()?;
22 Ok(Self(Some(id)))
23 }
24}
25
26#[derive(Parser)]
27#[command(about, long_about = None)]
28struct Args {
29 #[arg(requires = "old", requires = "new")]
31 repo: Option<PathBuf>,
32 old: Option<Side>,
34 new: Option<Side>,
36}
37
38fn main() -> girt::Result<()> {
39 let args = Args::parse();
40 let temporary;
41 let (repo, old, new) =
42 if let (Some(path), Some(old), Some(new)) = (args.repo, args.old, args.new) {
43 (Repository::open(path)?, old.0, new.0)
44 } else {
45 temporary = tempfile::tempdir()?;
46 let repo = Repository::init(
47 girt::ObjectFormat::Sha1,
48 temporary.path().join("sample"),
49 InitKind::Bare,
50 )?;
51
52 let store = repo.loose_objects();
53 let old_blob = store.write_blob(b"hello\n")?;
54 let new_blob = store.write_blob(b"hello, trees\n")?;
55
56 let old = store.write_tree(&Tree::new(
57 girt::ObjectFormat::Sha1,
58 vec![TreeEntry {
59 name: b"hello.txt".to_vec(),
60 mode: EntryMode::Blob,
61 id: old_blob,
62 }],
63 )?)?;
64 let new = store.write_tree(&Tree::new(
65 girt::ObjectFormat::Sha1,
66 vec![TreeEntry {
67 name: b"hello.txt".to_vec(),
68 mode: EntryMode::Executable,
69 id: new_blob,
70 }],
71 )?)?;
72
73 (repo, Some(old), Some(new))
74 };
75
76 let objects = repo.objects()?;
77 let changes = objects.compare_trees(old, new)?;
78
79 for change in changes {
80 println!(
82 "{} {}: {} -> {}",
83 change.status().letter(),
84 quote_path(&change.path),
85 describe(change.old),
86 describe(change.new)
87 );
88 }
89
90 Ok(())
91}
92
93fn describe(value: Option<TreeValue>) -> String {
94 match value {
95 Some(value) => format!("{} {}", value.mode.octal(), value.id),
96 None => "absent".into(),
97 }
98}