feat: create a blob object
This commit is contained in:
parent
bfc0e7f7b7
commit
82f4381d20
78
Cargo.lock
generated
78
Cargo.lock
generated
@ -64,6 +64,15 @@ version = "1.0.95"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "34ac096ce696dc2fcabef30516bb13c0a68a11d30131d3df6f04711467681b04"
|
||||
|
||||
[[package]]
|
||||
name = "block-buffer"
|
||||
version = "0.10.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71"
|
||||
dependencies = [
|
||||
"generic-array",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.0"
|
||||
@ -116,6 +125,15 @@ version = "1.0.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990"
|
||||
|
||||
[[package]]
|
||||
name = "cpufeatures"
|
||||
version = "0.2.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crc32fast"
|
||||
version = "1.4.2"
|
||||
@ -125,6 +143,26 @@ dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crypto-common"
|
||||
version = "0.1.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3"
|
||||
dependencies = [
|
||||
"generic-array",
|
||||
"typenum",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "digest"
|
||||
version = "0.10.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
|
||||
dependencies = [
|
||||
"block-buffer",
|
||||
"crypto-common",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "flate2"
|
||||
version = "1.0.35"
|
||||
@ -135,6 +173,16 @@ dependencies = [
|
||||
"miniz_oxide",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "generic-array"
|
||||
version = "0.14.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
|
||||
dependencies = [
|
||||
"typenum",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "heck"
|
||||
version = "0.5.0"
|
||||
@ -153,6 +201,12 @@ version = "1.70.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.169"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a"
|
||||
|
||||
[[package]]
|
||||
name = "mg"
|
||||
version = "0.1.0"
|
||||
@ -161,6 +215,7 @@ dependencies = [
|
||||
"clap",
|
||||
"flate2",
|
||||
"hex",
|
||||
"sha1",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
@ -197,6 +252,17 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sha1"
|
||||
version = "0.10.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cpufeatures",
|
||||
"digest",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "strsim"
|
||||
version = "0.11.1"
|
||||
@ -234,6 +300,12 @@ dependencies = [
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "typenum"
|
||||
version = "1.17.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.16"
|
||||
@ -246,6 +318,12 @@ version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
|
||||
|
||||
[[package]]
|
||||
name = "version_check"
|
||||
version = "0.9.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.59.0"
|
||||
|
@ -8,4 +8,5 @@ anyhow = "1.0.95"
|
||||
clap = { version = "4.5.27", features = ["derive", "string"] }
|
||||
flate2 = "1.0.35"
|
||||
hex = "0.4.3"
|
||||
sha1 = "0.10.6"
|
||||
thiserror = "2.0.11"
|
||||
|
12
src/main.rs
12
src/main.rs
@ -10,6 +10,7 @@ mod kind;
|
||||
mod object;
|
||||
|
||||
use crate::object::read_object;
|
||||
use crate::object::write_object;
|
||||
|
||||
#[derive(Parser)]
|
||||
#[command(name = "mg", about = "A simple git clone")]
|
||||
@ -31,6 +32,11 @@ enum Command {
|
||||
/// The object to display
|
||||
hash: String,
|
||||
},
|
||||
/// Write a blob object
|
||||
WriteBlob {
|
||||
/// The file to write
|
||||
file: PathBuf,
|
||||
},
|
||||
}
|
||||
|
||||
fn default_init_path() -> PathBuf {
|
||||
@ -61,9 +67,13 @@ fn main() -> Result<(), Error> {
|
||||
Err(e) => eprintln!("Failed to initialize repository: {}", e),
|
||||
},
|
||||
Command::CatFile { hash } => match read_object(&path, &hash) {
|
||||
Ok(mut obj) => println!("{}", obj.string()?),
|
||||
Ok(mut obj) => print!("{}", obj.string()?),
|
||||
Err(e) => eprintln!("Failed to read object: {}", e),
|
||||
},
|
||||
Command::WriteBlob { file } => match write_object(&path, &file) {
|
||||
Ok(hash) => println!("{}", hash),
|
||||
Err(e) => eprintln!("Failed to write object: {}", e),
|
||||
},
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
@ -1,7 +1,14 @@
|
||||
use anyhow::{Context, Result};
|
||||
use std::{fs::File, io::BufRead, path::Path};
|
||||
|
||||
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::{
|
||||
fs::{create_dir, File},
|
||||
io::{copy, BufRead},
|
||||
os::unix::fs::MetadataExt,
|
||||
path::Path,
|
||||
};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Object<Reader> {
|
||||
@ -61,6 +68,60 @@ pub fn read_object(path: &Path, object: &str) -> Result<Object<impl BufRead>> {
|
||||
})
|
||||
}
|
||||
|
||||
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()?;
|
||||
let file_canonical = match file_path.canonicalize() {
|
||||
Ok(path) => path,
|
||||
Err(_) => return Ok(false),
|
||||
};
|
||||
|
||||
// Check if file_path starts with repo_path
|
||||
Ok(file_canonical.starts_with(repo_canonical))
|
||||
}
|
||||
|
||||
pub fn hash_file(file: &Path) -> Result<String> {
|
||||
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)?;
|
||||
let hash_bytes = hasher.finalize();
|
||||
|
||||
Ok(hex::encode(hash_bytes))
|
||||
}
|
||||
|
||||
pub fn write_object(repo_path: &Path, file: &Path) -> Result<String> {
|
||||
if !file.exists() || !is_path_in_repo(repo_path, file)? {
|
||||
return Err(anyhow!("path does not exist"));
|
||||
}
|
||||
|
||||
let hash = hash_file(file)?;
|
||||
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[..2]);
|
||||
|
||||
if !target_dir.exists() {
|
||||
create_dir(&target_dir).context("could not create directory in .git/objects")?;
|
||||
}
|
||||
|
||||
let target_file = target_dir.join(&hash[2..]);
|
||||
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")?;
|
||||
|
||||
Ok(hash)
|
||||
}
|
||||
|
||||
impl<R: BufRead> Object<R> {
|
||||
pub fn string(&mut self) -> Result<String> {
|
||||
let mut buf: Vec<u8> = Vec::new();
|
||||
|
Loading…
x
Reference in New Issue
Block a user