use anyhow::Result; use git2::{BranchType, Oid, Repository, Tree}; use std::collections::HashMap; const USAGE: &str = r#" whizpopper <path to original repo> <path to bfg'ed repo> <comma separated list of branches to check> "#; // If no other is supplied, then build up hashmap // If other is supplied, then additionally print diffs where they arise fn get_or_diff_file_hashes( tree: &Tree, other: Option<&HashMap<String, Oid>>, ) -> (HashMap<String, Oid>, i32) { let mut file_hashes: HashMap<String, Oid> = HashMap::new(); let mut return_code = 0; tree.walk(git2::TreeWalkMode::PreOrder, |parent_directory, entry| { if entry.kind().is_none() { return git2::TreeWalkResult::Skip; } let kind = entry.kind().unwrap(); if kind != git2::ObjectType::Tree && kind != git2::ObjectType::Blob { return git2::TreeWalkResult::Skip; }; let entry_name = entry.name().unwrap_or("Name not understood"); let is_dir = kind == git2::ObjectType::Tree; let file_path = format!( "{}{}{}", parent_directory, entry_name, if is_dir { "/" } else { "" } ); let oid = entry.id(); if let Some(other_hashes) = other { if !other_hashes.contains_key(&file_path) { return_code = 1; println!("{} is in new but not original", file_path); } else { let original_oid = other_hashes.get(&file_path); if original_oid != Some(&oid) { return_code = 1; println!( "{}: new id is {:?}, old id is {:?}", file_path, oid, original_oid ); } } }; file_hashes.insert(file_path, oid); git2::TreeWalkResult::Ok }) .unwrap(); (file_hashes, return_code) } fn main() -> Result<()> { let mut args = std::env::args().skip(1); if args.len() != 3 { eprintln!("{}", USAGE); std::process::exit(1); } let path_to_original = args.next().expect(USAGE); let path_to_bfged = args.next().expect(USAGE); let branches_str = args.next().expect(USAGE); let branches = branches_str.split(","); let original_repo = Repository::open(path_to_original)?; let bfged_repo = Repository::open(path_to_bfged)?; let mut exit_code = 0; for branch in branches { println!("checking branch {}", branch); let original_tree = original_repo .find_branch(branch, BranchType::Local)? .get() .peel_to_tree()?; let (original_hashes, _) = get_or_diff_file_hashes(&original_tree, None); let bfged_tree = bfged_repo .find_branch(branch, BranchType::Local)? .get() .peel_to_tree()?; let (bfged_hashes, branch_exit_code) = get_or_diff_file_hashes(&bfged_tree, Some(&original_hashes)); if branch_exit_code != 0 { exit_code = branch_exit_code } for (path, _) in &original_hashes { if !bfged_hashes.contains_key(path) { exit_code = 1; println!("original has {} but new does not", path); } } } if exit_code != 0 { println!("some content was unexpectedly different"); std::process::exit(exit_code); } Ok(()) }