修改Blob逻辑,主要变更为 Blob 与 objects文件解构。store作为与objects文件交互的唯一依赖。具体为:

1. Blob使用Content新建而不是path
2. workdir的读写能力由util::read|write workfile提供
3. 判断文件是否更改不直接计算hash,经由新建一个不保存的Blob进行。现在Hash算法只由Store决定。
This commit is contained in:
HouXiaoxuan
2023-12-28 22:23:44 +08:00
parent b3aea1d0d1
commit f88ab0e33a
9 changed files with 91 additions and 67 deletions

View File

@@ -2,11 +2,7 @@ use base64::Engine;
use flate2::{read::GzDecoder, write::GzEncoder, Compression};
use std::io::{Read, Write};
use crate::{
models::Hash,
utils::{store, util},
};
use std::{fs, path::Path};
use crate::{models::Hash, utils::store};
/**Blob<br>
git中最基本的对象他储存一份文件的内容并使用hash作为标识符。
@@ -19,31 +15,46 @@ pub struct Blob {
impl Blob {
/// 从源文件新建blob对象并直接保存到/objects/中
pub fn new(file: &Path) -> Blob {
let data = fs::read_to_string(file).expect(format!("无法读取文件:{:?}", file).as_str());
pub fn new(data: String) -> Blob {
let mut blob = Blob { hash: "".to_string(), data };
blob.save();
blob
}
pub fn load(hash: &String) -> Blob {
/// 从源文件新建blob对象但不保存到/objects/中
pub fn dry_new(data: String) -> Blob {
let mut blob = Blob { hash: "".to_string(), data };
let s = store::Store::new();
let encoded = s.load(hash);
let hash: String = s.dry_save(&Blob::encode(blob.data.clone()));
blob.hash = hash;
blob
}
fn encode(data: String) -> String {
let mut cmopress_encoder = GzEncoder::new(Vec::new(), Compression::default());
cmopress_encoder.write_all(data.as_bytes()).unwrap();
let compressed_data = cmopress_encoder.finish().unwrap();
base64::engine::general_purpose::STANDARD_NO_PAD.encode(&compressed_data)
}
fn decode(encoded: String) -> String {
let compressed_data = base64::engine::general_purpose::STANDARD_NO_PAD.decode(&encoded).unwrap();
let mut decompress_decoder = GzDecoder::new(&compressed_data[..]);
let mut data = String::new();
decompress_decoder.read_to_string(&mut data).unwrap();
data
}
pub fn load(hash: &String) -> Blob {
let s = store::Store::new();
let encoded_data = s.load(hash);
let data = Blob::decode(encoded_data);
Blob { hash: hash.clone(), data }
}
/// 写入文件
pub fn save(&mut self) -> Hash {
let s = store::Store::new();
let mut cmopress_encoder = GzEncoder::new(Vec::new(), Compression::default());
cmopress_encoder.write_all(self.data.as_bytes()).unwrap();
let compressed_data = cmopress_encoder.finish().unwrap();
let encoded_data = base64::engine::general_purpose::STANDARD_NO_PAD.encode(&compressed_data);
let hash: String = s.save(&encoded_data);
let hash: String = s.save(&Blob::encode(self.data.clone()));
self.hash = hash;
self.hash.clone()
}
@@ -51,20 +62,21 @@ impl Blob {
pub fn get_hash(&self) -> String {
self.hash.clone()
}
pub fn get_content(&self) -> String {
self.data.clone()
}
}
#[cfg(test)]
mod test {
use std::path::PathBuf;
use crate::utils::test_util;
#[test]
fn test_save_and_load() {
test_util::setup_test_with_clean_mit();
let test_data = "hello world";
test_util::ensure_test_file(&PathBuf::from("a.txt"), Some(test_data));
let blob = super::Blob::new(&PathBuf::from("a.txt"));
let blob = super::Blob::new(test_data.into());
let blob2 = super::Blob::load(&blob.hash);
assert_eq!(blob2.get_hash(), blob.get_hash());

View File

@@ -233,12 +233,12 @@ mod tests {
test_util::setup_test_with_clean_mit();
let index = Index::get_instance();
let path = PathBuf::from("../mit_test_storage/.mit/HEAD"); //测试../相对路径的处理
index.add(path.clone(), FileMetaData::new(&Blob::new(&path), &path));
index.add(path.clone(), FileMetaData::new(&Blob::new(util::read_workfile(&path)), &path));
let = "中文路径.txt";
test_util::ensure_test_file(Path::new(), None);
let path = PathBuf::from();
index.add(path.clone(), FileMetaData::new(&Blob::new(&path), &path));
index.add(path.clone(), FileMetaData::new(&Blob::new(util::read_workfile(&path)), &path));
index.save();
println!("{:?}", index.entries);
}
@@ -248,7 +248,7 @@ mod tests {
test_util::setup_test_with_empty_workdir();
let index = Index::get_instance();
let path = PathBuf::from(".mit/HEAD");
index.add(path.clone(), FileMetaData::new(&Blob::new(&path), &path));
index.add(path.clone(), FileMetaData::new(&Blob::new(util::read_workfile(&path)), &path));
assert!(Index::new().is_empty()); //未保存前新读取的index应该是空的
index.save();
assert!(!Index::new().is_empty()); //保存后新读取的index不是空的

View File

@@ -137,7 +137,7 @@ mod test {
use crate::{
models::*,
utils::test_util,
utils::{test_util, util},
};
#[test]
@@ -147,8 +147,7 @@ mod test {
for test_file in vec!["b.txt", "mit_src/a.txt", "test/test.txt"] {
let test_file = PathBuf::from(test_file);
test_util::ensure_test_file(&test_file, None);
index.add(test_file.clone(), FileMetaData::new(&Blob::new(&test_file), &test_file));
index.add(test_file.clone(), FileMetaData::new(&Blob::new(&test_file), &test_file));
index.add(test_file.clone(), FileMetaData::new(&Blob::new(util::read_workfile(&test_file)), &test_file));
}
let tree = Tree::new(&index);
@@ -164,7 +163,7 @@ mod test {
for test_file in test_files.clone() {
let test_file = PathBuf::from(test_file);
test_util::ensure_test_file(&test_file, None);
index.add(test_file.clone(), FileMetaData::new(&Blob::new(&test_file), &test_file));
index.add(test_file.clone(), FileMetaData::new(&Blob::new(util::read_workfile(&test_file)), &test_file));
}
let tree = Tree::new(&index);
@@ -185,9 +184,9 @@ mod test {
for test_file in test_files.clone() {
let test_file = PathBuf::from(test_file);
test_util::ensure_test_file(&test_file, None);
let blob = Blob::new(&test_file);
let blob = Blob::new(util::read_workfile(&test_file));
test_blobs.push(blob.clone());
index.add(test_file.clone(), FileMetaData::new(&Blob::new(&test_file), &test_file));
index.add(test_file.clone(), FileMetaData::new(&Blob::new(util::read_workfile(&test_file)), &test_file));
}
let tree = Tree::new(&index);