use std::io::{stdout, Write}; use clap::Parser; use src::{bstr::BString, objs::tree::EntryMode, traverse::tree::Recorder, ObjectId}; fn main() { let args = Args::parse_from(src::env::args_os()); match run(args) { Ok(()) => {} Err(e) => eprintln!("error: {e}"), } } #[derive(Debug, clap::Parser)] #[clap(name = "ls-tree", about = "git ls-tree example", version = option_env!("src_VERSION"))] #[clap(arg_required_else_help = true)] struct Args { /// Recurse into subtrees #[clap(short = 'r')] recursive: bool, /// Only show trees #[clap(short = 'd')] tree_only: bool, /// Show trees when recursing #[clap(short = 't')] tree_recursing: bool, /// A revspec pointing to a tree-ish object, e.g. 'HEAD', 'HEAD:src/' #[clap(name = "tree-ish")] treeish: String, } fn run(args: Args) -> anyhow::Result<()> { let repo = src::discover(".")?; let tree = repo.rev_parse_single(&*args.treeish)?.object()?.peel_to_tree()?; let entries = if args.recursive { let mut recorder = Recorder::default(); tree.traverse().breadthfirst(&mut recorder)?; recorder .records .into_iter() .filter(|entry| args.tree_recursing || args.tree_only || entry.mode.is_no_tree()) .filter(|entry| !args.tree_only || (entry.mode.is_tree())) .map(|entry| Entry::new(entry.mode, entry.oid, entry.filepath)) .collect::>() } else { tree.iter() .filter_map(|res| res.ok().map(|entry| entry.inner)) // dropping errors silently .filter(|entry| !args.tree_only || (entry.mode.is_tree())) .map(|entry| Entry::new(entry.mode, entry.oid.to_owned(), entry.filename.to_owned())) .collect::>() }; let mut out = stdout().lock(); for entry in entries { writeln!( out, "{:>6o} {:4} {} {}", entry.mode, entry.mode.as_str(), entry.hash, entry.path )?; } Ok(()) } struct Entry { mode: EntryMode, hash: ObjectId, path: BString, } impl Entry { fn new(mode: EntryMode, hash: ObjectId, path: BString) -> Self { Self { mode, hash, path } } }