feat: adding hash-object
This commit is contained in:
parent
daf5fa3272
commit
de4c366ebb
11
src/kind.rs
11
src/kind.rs
@ -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)
|
||||
}
|
||||
}
|
||||
|
10
src/main.rs
10
src/main.rs
@ -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(())
|
||||
|
@ -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(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -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];
|
||||
|
Loading…
x
Reference in New Issue
Block a user