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};
|
use anyhow::{anyhow, Result};
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@ -30,13 +32,16 @@ impl Kind {
|
|||||||
Kind::Symlink => "120000",
|
Kind::Symlink => "120000",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn string(&self) -> &str {
|
impl fmt::Display for Kind {
|
||||||
match self {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
let kind = match self {
|
||||||
Kind::Blob(_) => "blob",
|
Kind::Blob(_) => "blob",
|
||||||
Kind::Commit => "commit",
|
Kind::Commit => "commit",
|
||||||
Kind::Tree => "tree",
|
Kind::Tree => "tree",
|
||||||
Kind::Symlink => "symlink",
|
Kind::Symlink => "symlink",
|
||||||
}
|
};
|
||||||
|
write!(f, "{}", kind)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
10
src/main.rs
10
src/main.rs
@ -1,4 +1,5 @@
|
|||||||
use anyhow::{Error, Result};
|
use anyhow::{Error, Result};
|
||||||
|
use object::hash_object;
|
||||||
use repository::default_init_path;
|
use repository::default_init_path;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
@ -72,6 +73,11 @@ enum Command {
|
|||||||
/// The pack index file to dump
|
/// The pack index file to dump
|
||||||
pack_id: String,
|
pack_id: String,
|
||||||
},
|
},
|
||||||
|
/// Hash an object
|
||||||
|
HashObject {
|
||||||
|
/// The object to hash
|
||||||
|
file: PathBuf,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() -> Result<(), Error> {
|
fn main() -> Result<(), Error> {
|
||||||
@ -128,6 +134,10 @@ fn main() -> Result<(), Error> {
|
|||||||
Ok(_) => (),
|
Ok(_) => (),
|
||||||
Err(e) => eprintln!("Failed to dump pack index file: {}", e),
|
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(())
|
Ok(())
|
||||||
|
@ -2,6 +2,7 @@ use crate::repository::Repository;
|
|||||||
use crate::{error::RuntimeError, kind::Kind};
|
use crate::{error::RuntimeError, kind::Kind};
|
||||||
use anyhow::{anyhow, Context, Result};
|
use anyhow::{anyhow, Context, Result};
|
||||||
use flate2::{write::ZlibEncoder, Compression};
|
use flate2::{write::ZlibEncoder, Compression};
|
||||||
|
|
||||||
use sha1::{Digest, Sha1};
|
use sha1::{Digest, Sha1};
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
use std::{
|
use std::{
|
||||||
@ -82,7 +83,7 @@ impl Repository {
|
|||||||
|
|
||||||
pub fn write_object(&self, kind: Kind, content: &[u8]) -> Result<[u8; 20]> {
|
pub fn write_object(&self, kind: Kind, content: &[u8]) -> Result<[u8; 20]> {
|
||||||
let mut hasher = Sha1::new();
|
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);
|
hasher.update(content);
|
||||||
let hash = hasher.finalize().into();
|
let hash = hasher.finalize().into();
|
||||||
let hash_str = hex::encode(hash);
|
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 file_out_fd = File::create(target_file).context("could not open target file")?;
|
||||||
|
|
||||||
let mut zlib_out = ZlibEncoder::new(file_out_fd, Compression::default());
|
let mut zlib_out = ZlibEncoder::new(file_out_fd, Compression::default());
|
||||||
write!(zlib_out, "{} {}\0", kind.string(), content.len())
|
write!(zlib_out, "{} {}\0", kind, content.len()).context("could not write header")?;
|
||||||
.context("could not write header")?;
|
|
||||||
zlib_out.write_all(content)?;
|
zlib_out.write_all(content)?;
|
||||||
zlib_out
|
zlib_out
|
||||||
.finish()
|
.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> {
|
fn is_path_in_repo(repo_path: &Path, file_path: &Path) -> Result<bool> {
|
||||||
// Convert both paths to absolute paths
|
// Convert both paths to absolute paths
|
||||||
let repo_canonical = repo_path.canonicalize()?;
|
let repo_canonical = repo_path.canonicalize()?;
|
||||||
@ -178,7 +190,7 @@ impl<R: BufRead> Object<R> {
|
|||||||
format!(
|
format!(
|
||||||
"{:0>6} {} {} {:name_len$}",
|
"{:0>6} {} {} {:name_len$}",
|
||||||
entry.mode,
|
entry.mode,
|
||||||
entry.kind.string(),
|
entry.kind,
|
||||||
hash,
|
hash,
|
||||||
entry.name,
|
entry.name,
|
||||||
name_len = max_name_len
|
name_len = max_name_len
|
||||||
@ -193,3 +205,31 @@ impl<R: BufRead> Object<R> {
|
|||||||
Ok(res)
|
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];
|
let mut buf = [0u8; 256 * 4];
|
||||||
file.read_exact(&mut buf)?;
|
file.read_exact(&mut buf)?;
|
||||||
|
|
||||||
for idx in 0..256 {
|
for (idx, fanout_record) in fanout_table.iter_mut().enumerate() {
|
||||||
let offset = idx * 4;
|
num_objects = u32::from_be_bytes(buf[idx * 4..idx * 4 + 4].try_into().unwrap());
|
||||||
num_objects = u32::from_be_bytes(buf[offset..offset + 4].try_into().unwrap());
|
*fanout_record = num_objects;
|
||||||
fanout_table[idx] = num_objects;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut names = vec![0u8; 20 * num_objects as usize];
|
let mut names = vec![0u8; 20 * num_objects as usize];
|
||||||
|
Loading…
x
Reference in New Issue
Block a user