diff --git a/.gitignore b/.gitignore index ec9828c..795c7fd 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ /target +/test .rgit diff --git a/src/base.rs b/src/base.rs index e9ac30b..a02956e 100644 --- a/src/base.rs +++ b/src/base.rs @@ -1,4 +1,7 @@ +use std::collections::HashMap; use std::fs; +use std::io; +use std::path::Path; #[path = "data.rs"] mod data; @@ -45,6 +48,19 @@ pub fn write_tree(directory: String) -> String { return data::hash_object(&tree.into_bytes(), "tree".to_owned()); } +pub fn read_tree(oid: String) { + for (path, object_id) in get_tree(oid, "./".to_owned()).iter() { + let mut dirs = Path::new(path).ancestors(); + dirs.next(); + + let dir = dirs.next().unwrap().to_str().unwrap(); + + fs::create_dir_all(dir).expect("Cannot create required dirs"); + fs::write(path, data::get_object(object_id.clone(), "".to_owned())) + .expect("Cannot write required object"); + } +} + fn is_ignored(path: &String) -> bool { if path.contains(".rgit") { true @@ -52,3 +68,36 @@ fn is_ignored(path: &String) -> bool { false } } + +fn tree_entries(oid: String) -> Vec<(String, String, String)> { + let mut entries: Vec<(String, String, String)> = vec![]; + let tree_data = data::get_object(oid, "tree".to_owned()); + for line in tree_data.split_terminator("\n") { + let items: Vec<&str> = line.splitn(3, " ").collect(); + entries.push(( + items[0].to_owned(), // _type + items[1].to_owned(), // oid + items[2].to_owned(), // name + )); + } + return entries; +} + +fn get_tree(oid: String, base_path: String) -> HashMap { + let mut result = HashMap::new(); + for entry in tree_entries(oid) { + // _type, oid, name + assert!(entry.2.find("/").is_none()); + assert!(entry.2 != ".."); + assert!(entry.2 != "."); + let path = base_path.clone() + entry.2.as_str(); + if entry.0 == "blob".to_owned() { + result.insert(path.clone(), entry.1.clone()); + } else if entry.0 == "tree".to_owned() { + result.extend(get_tree(entry.1, format!("{}/", path))); + } else { + panic!("Unknown tree entry: {}", entry.0); + } + } + result +} diff --git a/src/data.rs b/src/data.rs index cdc48ce..ea9d174 100644 --- a/src/data.rs +++ b/src/data.rs @@ -35,6 +35,7 @@ pub fn get_object(hash: String, expected: String) -> String { if expected != "".to_owned() { // Compare the type + content.pop(); assert_eq!(expected, content); } diff --git a/src/main.rs b/src/main.rs index 33bb98b..0d540e9 100644 --- a/src/main.rs +++ b/src/main.rs @@ -23,6 +23,11 @@ fn main() { SubCommand::with_name("write-tree") .about("write the current working directory to the database"), ) + .subcommand( + SubCommand::with_name("read-tree") + .about("writes a given tree to the working directory") + .arg(Arg::with_name("oid").index(1).required(true)), + ) .get_matches(); match matches.subcommand_name() { @@ -30,6 +35,7 @@ fn main() { Some("hash-object") => hash_object(matches), Some("cat-file") => cat_file(matches), Some("write-tree") => write_tree(), + Some("read-tree") => read_tree(matches), _ => println!("unknown sub command"), } } @@ -63,3 +69,10 @@ fn cat_file(matches: ArgMatches) { fn write_tree() { println!("{}", base::write_tree(".".to_owned())); } + +fn read_tree(matches: ArgMatches) { + if let Some(cmd_matches) = matches.subcommand_matches("read-tree") { + let oid = cmd_matches.value_of("oid").unwrap(); + base::read_tree(oid.to_owned()); + } +}