use std::path::PathBuf;
use std::str::FromStr;
use clap::Parser;
use girt::display::quote_path;
use girt::{EntryMode, InitKind, ObjectId, Repository, Tree, TreeEntry, TreeValue};
#[derive(Clone, Copy, Debug)]
struct Side(Option<ObjectId>);
impl FromStr for Side {
type Err = girt::ParseObjectIdError;
fn from_str(text: &str) -> Result<Self, Self::Err> {
if text == "-" {
return Ok(Self(None));
}
let id = text.parse()?;
Ok(Self(Some(id)))
}
}
#[derive(Parser)]
#[command(about, long_about = None)]
struct Args {
#[arg(requires = "old", requires = "new")]
repo: Option<PathBuf>,
old: Option<Side>,
new: Option<Side>,
}
fn main() -> girt::Result<()> {
let args = Args::parse();
let temporary;
let (repo, old, new) =
if let (Some(path), Some(old), Some(new)) = (args.repo, args.old, args.new) {
(Repository::open(path)?, old.0, new.0)
} else {
temporary = tempfile::tempdir()?;
let repo = Repository::init(
girt::ObjectFormat::Sha1,
temporary.path().join("sample"),
InitKind::Bare,
)?;
let store = repo.loose_objects();
let old_blob = store.write_blob(b"hello\n")?;
let new_blob = store.write_blob(b"hello, trees\n")?;
let old = store.write_tree(&Tree::new(
girt::ObjectFormat::Sha1,
vec![TreeEntry {
name: b"hello.txt".to_vec(),
mode: EntryMode::Blob,
id: old_blob,
}],
)?)?;
let new = store.write_tree(&Tree::new(
girt::ObjectFormat::Sha1,
vec![TreeEntry {
name: b"hello.txt".to_vec(),
mode: EntryMode::Executable,
id: new_blob,
}],
)?)?;
(repo, Some(old), Some(new))
};
let objects = repo.objects()?;
let changes = objects.compare_trees(old, new)?;
for change in changes {
println!(
"{} {}: {} -> {}",
change.status().letter(),
quote_path(&change.path),
describe(change.old),
describe(change.new)
);
}
Ok(())
}
fn describe(value: Option<TreeValue>) -> String {
match value {
Some(value) => format!("{} {}", value.mode.octal(), value.id),
None => "absent".into(),
}
}