feat: adding hash-object
All checks were successful
CI checks / Clippy (push) Successful in 27s
CI checks / Format (push) Successful in 25s

This commit is contained in:
Patrick MARIE 2025-02-08 23:16:24 +01:00
parent daf5fa3272
commit de4c366ebb
Signed by: mycroft
GPG Key ID: BB519E5CD8E7BFA7
4 changed files with 65 additions and 11 deletions

View File

@ -1,3 +1,5 @@
use std::fmt;
use anyhow::{anyhow, Result};
#[derive(Debug)]
@ -30,13 +32,16 @@ impl Kind {
Kind::Symlink => "120000",
}
}
}
pub fn string(&self) -> &str {
match self {
impl fmt::Display for Kind {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let kind = match self {
Kind::Blob(_) => "blob",
Kind::Commit => "commit",
Kind::Tree => "tree",
Kind::Symlink => "symlink",
}
};
write!(f, "{}", kind)
}
}

View File

@ -1,4 +1,5 @@
use anyhow::{Error, Result};
use object::hash_object;
use repository::default_init_path;
use std::path::PathBuf;
@ -72,6 +73,11 @@ enum Command {
/// The pack index file to dump
pack_id: String,
},
/// Hash an object
HashObject {
/// The object to hash
file: PathBuf,
},
}
fn main() -> Result<(), Error> {
@ -128,6 +134,10 @@ fn main() -> Result<(), Error> {
Ok(_) => (),
Err(e) => eprintln!("Failed to dump pack index file: {}", e),
},
Command::HashObject { file } => match hash_object(&file) {
Ok(hash) => println!("{}", hex::encode(hash)),
Err(e) => eprintln!("Failed to hash object: {}", e),
},
}
Ok(())

View File

@ -2,6 +2,7 @@ use crate::repository::Repository;
use crate::{error::RuntimeError, kind::Kind};
use anyhow::{anyhow, Context, Result};
use flate2::{write::ZlibEncoder, Compression};
use sha1::{Digest, Sha1};
use std::io::Write;
use std::{
@ -82,7 +83,7 @@ impl Repository {
pub fn write_object(&self, kind: Kind, content: &[u8]) -> Result<[u8; 20]> {
let mut hasher = Sha1::new();
hasher.update(format!("{} {}\0", kind.string(), content.len()).as_bytes());
hasher.update(format!("{} {}\0", kind, content.len()).as_bytes());
hasher.update(content);
let hash = hasher.finalize().into();
let hash_str = hex::encode(hash);
@ -100,8 +101,7 @@ impl Repository {
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, "{} {}\0", kind.string(), content.len())
.context("could not write header")?;
write!(zlib_out, "{} {}\0", kind, content.len()).context("could not write header")?;
zlib_out.write_all(content)?;
zlib_out
.finish()
@ -111,6 +111,18 @@ impl Repository {
}
}
pub fn hash_object(file: &Path) -> Result<[u8; 20]> {
let content = std::fs::read(file)?;
let kind = Kind::Blob(false);
let mut hasher = Sha1::new();
hasher.update(format!("{} {}\0", kind, content.len()).as_bytes());
hasher.update(content);
let hash = hasher.finalize().into();
Ok(hash)
}
fn is_path_in_repo(repo_path: &Path, file_path: &Path) -> Result<bool> {
// Convert both paths to absolute paths
let repo_canonical = repo_path.canonicalize()?;
@ -178,7 +190,7 @@ impl<R: BufRead> Object<R> {
format!(
"{:0>6} {} {} {:name_len$}",
entry.mode,
entry.kind.string(),
entry.kind,
hash,
entry.name,
name_len = max_name_len
@ -193,3 +205,31 @@ impl<R: BufRead> Object<R> {
Ok(res)
}
}
#[cfg(test)]
mod tests {
use super::*;
use hex::FromHex;
use std::io::Cursor;
#[test]
fn test_object_string() {
let data = b"hello";
let _obj = Object {
kind: Kind::Blob(true),
_size: 5,
data: Cursor::new(data),
};
let temp_file = std::env::temp_dir().join("temp_file");
let mut file = File::create(&temp_file).unwrap();
file.write_all(data).unwrap();
let res = hash_object(&temp_file);
assert_eq!(
res.unwrap(),
<[u8; 20]>::from_hex("b6fc4c620b67d95f953a5c1c1230aaab5db5a1b0").unwrap(),
);
}
}

View File

@ -378,10 +378,9 @@ impl Repository {
let mut buf = [0u8; 256 * 4];
file.read_exact(&mut buf)?;
for idx in 0..256 {
let offset = idx * 4;
num_objects = u32::from_be_bytes(buf[offset..offset + 4].try_into().unwrap());
fanout_table[idx] = num_objects;
for (idx, fanout_record) in fanout_table.iter_mut().enumerate() {
num_objects = u32::from_be_bytes(buf[idx * 4..idx * 4 + 4].try_into().unwrap());
*fanout_record = num_objects;
}
let mut names = vec![0u8; 20 * num_objects as usize];