//! Read Git object types, sizes, contents, and tree entries without changing repository state. use crate::{Result, repo::error}; use std::io::Write; pub(crate) fn cat_file( repo: &gix::Repository, revision: &str, kind: bool, size: bool, exists: bool, ) -> Result<()> { let object = repo .rev_parse_single(revision) .map_err(|e| error("cat-file", e))? .object() .map_err(|e| error("cat-file", e))?; if exists { return Ok(()); } if kind { println!("{}", object.kind); } else if size { println!("{}", object.data.len()); } else if object.kind == gix::objs::Kind::Tree { ls_tree(repo, revision, false, false, None)?; } else { std::io::stdout() .write_all(&object.data) .map_err(|e| error("cat-file", e))?; } Ok(()) } pub(crate) fn ls_tree( repo: &gix::Repository, revision: &str, recursive: bool, names: bool, path: Option<&str>, ) -> Result<()> { let id = crate::local::tree(repo, revision)?; fn walk( repo: &gix::Repository, id: gix::ObjectId, prefix: &str, recursive: bool, names: bool, filter: Option<&str>, ) -> Result<()> { let tree = repo.find_tree(id).map_err(|e| error("ls-tree", e))?; for entry in tree.iter() { let entry = entry.map_err(|e| error("ls-tree", e))?; let filename = format!("{prefix}{}", entry.filename()); let directory = entry.mode().is_tree(); let descend = directory && (recursive || filter.is_some_and(|path| path.starts_with(&format!("{filename}/")))); if descend { walk( repo, entry.oid().to_owned(), &format!("{filename}/"), recursive, names, filter, )?; } else if filter.is_none_or(|path| { filename == path || filename.starts_with(&format!("{}/", path.trim_end_matches('/'))) }) { if names { println!("{filename}"); } else { let kind = if directory { "tree" } else if entry.mode().is_commit() { "commit" } else { "blob" }; println!( "{:06o} {kind} {}\t{filename}", entry.mode().kind() as u32, entry.oid() ); } } } Ok(()) } walk(repo, id, "", recursive, names, path) }