feat: write-tree
This commit is contained in:
parent
e6ab0bebd6
commit
4b0902c648
@ -26,7 +26,7 @@ impl Kind {
|
||||
Kind::Blob(false) => "100644",
|
||||
Kind::Blob(true) => "100755",
|
||||
Kind::Commit => "160000",
|
||||
Kind::Tree => "040000",
|
||||
Kind::Tree => "40000",
|
||||
Kind::Symlink => "120000",
|
||||
}
|
||||
}
|
||||
|
@ -10,7 +10,7 @@ mod kind;
|
||||
mod object;
|
||||
mod tree;
|
||||
|
||||
use crate::object::{read_object, write_object};
|
||||
use crate::object::{read_object, write_blob};
|
||||
use crate::tree::write_tree;
|
||||
|
||||
#[derive(Parser)]
|
||||
@ -76,7 +76,7 @@ fn main() -> Result<(), Error> {
|
||||
Ok(mut obj) => print!("{}", obj.string()?),
|
||||
Err(e) => eprintln!("Failed to read object: {}", e),
|
||||
},
|
||||
Command::WriteBlob { file } => match write_object(&repo_path, &file) {
|
||||
Command::WriteBlob { file } => match write_blob(&repo_path, &file) {
|
||||
Ok(hash) => println!("{}", hex::encode(hash)),
|
||||
Err(e) => eprintln!("Failed to write object: {}", e),
|
||||
},
|
||||
|
@ -5,8 +5,7 @@ use sha1::{Digest, Sha1};
|
||||
use std::io::Write;
|
||||
use std::{
|
||||
fs::{create_dir, File},
|
||||
io::{copy, BufRead},
|
||||
os::unix::fs::MetadataExt,
|
||||
io::BufRead,
|
||||
path::Path,
|
||||
};
|
||||
|
||||
@ -80,44 +79,41 @@ fn is_path_in_repo(repo_path: &Path, file_path: &Path) -> Result<bool> {
|
||||
Ok(file_canonical.starts_with(repo_canonical))
|
||||
}
|
||||
|
||||
pub fn hash_file(file: &Path) -> Result<[u8; 20]> {
|
||||
let fd = File::open(file).context("opening file for sha1")?;
|
||||
let mut buf_reader = std::io::BufReader::new(fd);
|
||||
|
||||
let stat = file.metadata().context("stat on file")?;
|
||||
|
||||
let header = format!("blob {}\0", stat.size());
|
||||
|
||||
let mut hasher = Sha1::new();
|
||||
hasher.update(header.as_bytes());
|
||||
copy(&mut buf_reader, &mut hasher)?;
|
||||
|
||||
Ok(hasher.finalize().into())
|
||||
}
|
||||
|
||||
pub fn write_object(repo_path: &Path, file: &Path) -> Result<[u8; 20]> {
|
||||
pub fn write_blob(repo_path: &Path, file: &Path) -> Result<[u8; 20]> {
|
||||
if !file.exists() || !is_path_in_repo(repo_path, file)? {
|
||||
return Err(anyhow!("path does not exist"));
|
||||
}
|
||||
|
||||
let hash = hash_file(file)?;
|
||||
let content = std::fs::read(file)?;
|
||||
|
||||
Ok(write_object(repo_path, Kind::Blob(false), &content)?)
|
||||
}
|
||||
|
||||
pub fn write_object(repo_path: &Path, kind: Kind, content: &[u8]) -> Result<[u8; 20]> {
|
||||
let mut hasher = Sha1::new();
|
||||
hasher.update(format!("{} {}\0", kind.string(), content.len()).as_bytes());
|
||||
hasher.update(content);
|
||||
let hash = hasher.finalize().into();
|
||||
let hash_str = hex::encode(hash);
|
||||
let bytes_num = file.metadata()?.len();
|
||||
let mut buf_reader = std::io::BufReader::new(File::open(file)?);
|
||||
|
||||
let target_dir = repo_path.join(".git").join("objects").join(&hash_str[..2]);
|
||||
|
||||
if !target_dir.exists() {
|
||||
create_dir(&target_dir).context("could not create directory in .git/objects")?;
|
||||
}
|
||||
|
||||
let target_file = target_dir.join(&hash_str[2..]);
|
||||
if target_file.exists() {
|
||||
return Ok(hash);
|
||||
}
|
||||
|
||||
let file_out_fd = File::create(target_file).context("could not open target file")?;
|
||||
|
||||
let mut zlib_out = ZlibEncoder::new(file_out_fd, Compression::default());
|
||||
write!(zlib_out, "blob {}\0", bytes_num).context("could not write header")?;
|
||||
|
||||
std::io::copy(&mut buf_reader, &mut zlib_out).context("could not compress or write file")?;
|
||||
write!(zlib_out, "{} {}\0", kind.string(), content.len()).context("could not write header")?;
|
||||
zlib_out.write(content)?;
|
||||
zlib_out
|
||||
.finish()
|
||||
.context("could not compress or write file")?;
|
||||
|
||||
Ok(hash)
|
||||
}
|
||||
|
20
src/tree.rs
20
src/tree.rs
@ -1,12 +1,11 @@
|
||||
use anyhow::{Context, Result};
|
||||
use sha1::{Digest, Sha1};
|
||||
use std::os::unix::fs::MetadataExt;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use crate::kind::Kind;
|
||||
use crate::object::{hash_file, TreeObject};
|
||||
use crate::object::{write_blob, write_object, TreeObject};
|
||||
|
||||
pub fn write_tree(_repo_path: &PathBuf, path: &PathBuf) -> Result<[u8; 20]> {
|
||||
pub fn write_tree(repo_path: &PathBuf, path: &PathBuf) -> Result<[u8; 20]> {
|
||||
let mut entries = Vec::new();
|
||||
|
||||
let files = std::fs::read_dir(path)?;
|
||||
@ -20,10 +19,13 @@ pub fn write_tree(_repo_path: &PathBuf, path: &PathBuf) -> Result<[u8; 20]> {
|
||||
let kind;
|
||||
|
||||
if file_type.is_dir() {
|
||||
hash = write_tree(_repo_path, &file_path).context("could not write_tree of subtree")?;
|
||||
hash = write_tree(repo_path, &file_path).context("could not write_tree of subtree")?;
|
||||
kind = Kind::Tree;
|
||||
} else {
|
||||
hash = hash_file(&file_path)?;
|
||||
hash = write_blob(repo_path, &file_path).context(format!(
|
||||
"could not write object {:?}",
|
||||
file_path.file_name()
|
||||
))?;
|
||||
kind = Kind::Blob(file_path.metadata()?.mode() & 0o111 != 0);
|
||||
}
|
||||
|
||||
@ -46,11 +48,5 @@ pub fn write_tree(_repo_path: &PathBuf, path: &PathBuf) -> Result<[u8; 20]> {
|
||||
out.extend_from_slice(&entry.hash);
|
||||
}
|
||||
|
||||
let header = format!("tree {}\0", out.len());
|
||||
|
||||
let mut hasher = Sha1::new();
|
||||
hasher.update(header);
|
||||
hasher.update(out);
|
||||
|
||||
Ok(hasher.finalize().into())
|
||||
write_object(repo_path, Kind::Tree, &out).context("Write")
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user