diff --git a/mercury/Cargo.toml b/mercury/Cargo.toml index 10056d255..460078837 100644 --- a/mercury/Cargo.toml +++ b/mercury/Cargo.toml @@ -37,6 +37,7 @@ encoding_rs = { workspace = true } rayon = { workspace = true } reqwest = { workspace = true } ring = { workspace = true } +serde_json.workspace = true [dev-dependencies] tokio = { workspace = true, features = ["full"] } diff --git a/mercury/src/hash.rs b/mercury/src/hash.rs index e691290d1..ae4a3ce78 100644 --- a/mercury/src/hash.rs +++ b/mercury/src/hash.rs @@ -97,7 +97,12 @@ impl SHA1 { let h = sha1::Sha1::digest(data); SHA1::from_bytes(h.as_slice()) } - + /// Create a Hash from the object type and data + /// This function is used to create a SHA1 hash from the object type and data. + /// It constructs a byte vector that includes the object type, the size of the data, + /// and the data itself, and then computes the SHA1 hash of this byte vector. + /// + /// Hash compute <- {Object Type}+{ }+{Object Size(before compress)}+{\x00}+{Object Content(before compress)} pub fn from_type_and_data(object_type: ObjectType, data: &[u8]) -> SHA1 { let mut d: Vec = Vec::new(); d.extend(object_type.to_data().unwrap()); @@ -213,7 +218,7 @@ mod tests { Ok(hash) => { assert_eq!(hash.to_string(), "8ab686eafeb1f44702738c8b0f24f2567c36da6d"); } - Err(e) => println!("Error: {}", e), + Err(e) => println!("Error: {e}"), } } @@ -225,7 +230,7 @@ mod tests { Ok(hash) => { assert_eq!(hash.to_string(), "8ab686eafeb1f44702738c8b0f24f2567c36da6d"); } - Err(e) => println!("Error: {}", e), + Err(e) => println!("Error: {e}"), } } @@ -243,7 +248,7 @@ mod tests { ] ); } - Err(e) => println!("Error: {}", e), + Err(e) => println!("Error: {e}"), } } } diff --git a/mercury/src/internal/index.rs b/mercury/src/internal/index.rs index 04178b514..ecf8ced26 100644 --- a/mercury/src/internal/index.rs +++ b/mercury/src/internal/index.rs @@ -481,7 +481,7 @@ mod tests { let index = Index::from_file("../tests/data/index/index-760").unwrap(); assert_eq!(index.size(), 760); for (_, entry) in index.entries.iter() { - println!("{}", entry); + println!("{entry}"); } } @@ -499,6 +499,6 @@ mod tests { let hash = SHA1::from_bytes(&[0; 20]); let workdir = Path::new("../"); let entry = IndexEntry::new_from_file(file, hash, workdir).unwrap(); - println!("{}", entry); + println!("{entry}"); } } diff --git a/mercury/src/internal/model/commit.rs b/mercury/src/internal/model/commit.rs index 7d1311274..5f9c24152 100644 --- a/mercury/src/internal/model/commit.rs +++ b/mercury/src/internal/model/commit.rs @@ -1,53 +1,15 @@ -use std::str::FromStr; + use callisto::{git_commit, mega_commit}; use common::utils::generate_id; use crate::{ - hash::SHA1, internal::{ - object::{commit::Commit, signature::Signature, ObjectTrait}, + object::{commit::Commit, ObjectTrait}, pack::entry::Entry, }, }; -impl From for Commit { - fn from(value: mega_commit::Model) -> Self { - Commit { - id: SHA1::from_str(&value.commit_id).unwrap(), - tree_id: SHA1::from_str(&value.tree).unwrap(), - parent_commit_ids: value - .parents_id - .as_array() - .unwrap() - .iter() - .map(|id| SHA1::from_str(id.as_str().unwrap()).unwrap()) - .collect(), - author: Signature::from_data(value.author.unwrap().into()).unwrap(), - committer: Signature::from_data(value.committer.unwrap().into()).unwrap(), - message: value.content.unwrap(), - } - } -} - -impl From for Commit { - fn from(value: git_commit::Model) -> Self { - Commit { - id: SHA1::from_str(&value.commit_id).unwrap(), - tree_id: SHA1::from_str(&value.tree).unwrap(), - parent_commit_ids: value - .parents_id - .as_array() - .unwrap() - .iter() - .map(|id| SHA1::from_str(id.as_str().unwrap()).unwrap()) - .collect(), - author: Signature::from_data(value.author.unwrap().into()).unwrap(), - committer: Signature::from_data(value.committer.unwrap().into()).unwrap(), - message: value.content.unwrap(), - } - } -} impl From for mega_commit::Model { fn from(value: Commit) -> Self { diff --git a/mercury/src/internal/object/blob.rs b/mercury/src/internal/object/blob.rs index 7aa6dd4ae..65531f75e 100644 --- a/mercury/src/internal/object/blob.rs +++ b/mercury/src/internal/object/blob.rs @@ -82,10 +82,11 @@ impl ObjectTrait for Blob { } impl Blob { + + /// Create a new Blob object from the given content string. + /// - This is a convenience method for creating a Blob from a string. + /// - It converts the string to bytes and then calls `from_content_bytes`. pub fn from_content(content: &str) -> Self { - // let blob_content = Cursor::new(utils::compress_zlib(content.as_bytes()).unwrap()); - // let mut buf = ReadBoxed::new(blob_content, ObjectType::Blob, content.len()); - // Blob::from_buf_read(&mut buf, content.len()) let content = content.as_bytes().to_vec(); Blob::from_content_bytes(content) } @@ -94,6 +95,7 @@ impl Blob { /// - some file content can't be represented as a string (UTF-8), so we need to use bytes. pub fn from_content_bytes(content: Vec) -> Self { Blob { + // Calculate the SHA1 hash from the type and content id: SHA1::from_type_and_data(ObjectType::Blob, &content), data: content, } diff --git a/mercury/src/internal/object/commit.rs b/mercury/src/internal/object/commit.rs index aed2dfcbf..107c97926 100644 --- a/mercury/src/internal/object/commit.rs +++ b/mercury/src/internal/object/commit.rs @@ -20,6 +20,8 @@ use crate::internal::object::signature::Signature; use crate::internal::object::ObjectTrait; use crate::internal::object::ObjectType; use bstr::ByteSlice; +use callisto::git_commit; +use callisto::mega_commit; use serde::Deserialize; use serde::Serialize; @@ -34,6 +36,7 @@ use serde::Serialize; /// - The author and committer fields contain the name, email address, timestamp and timezone. /// - The message field contains the commit message, which maybe include signed or DCO. #[derive(Eq, Debug, Clone, Serialize, Deserialize)] +#[non_exhaustive] pub struct Commit { pub id: SHA1, pub tree_id: SHA1, @@ -48,6 +51,8 @@ impl PartialEq for Commit { } } + + impl Display for Commit { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { writeln!(f, "tree: {}", self.tree_id)?; @@ -76,11 +81,24 @@ impl Commit { committer, message: message.to_string(), }; + // Calculate the hash of the commit object + // The hash is calculated from the type and data of the commit object let hash = SHA1::from_type_and_data(ObjectType::Commit, &commit.to_data().unwrap()); commit.id = hash; commit } + /// Creates a new commit object from a tree ID and a list of parent commit IDs. + /// This function generates the author and committer signatures using the current time + /// and a fixed email address. + /// It also sets the commit message to the provided string. + /// # Arguments + /// - `tree_id`: The SHA1 hash of the tree object that this commit points to. + /// - `parent_commit_ids`: A vector of SHA1 hashes of the parent commits. + /// - `message`: A string containing the commit message. + /// # Returns + /// A new `Commit` object with the specified tree ID, parent commit IDs, and commit message. + /// The author and committer signatures are generated using the current time and a fixed email address. pub fn from_tree_id(tree_id: SHA1, parent_commit_ids: Vec, message: &str) -> Commit { let author = Signature::from_data( format!( @@ -214,3 +232,51 @@ impl ObjectTrait for Commit { Ok(data) } } +fn commit_from_model( + commit_id: &str, + tree: &str, + parents_id: &serde_json::Value, + author: Option, + committer: Option, + message: Option, +) -> Commit { + Commit { + id: SHA1::from_str(commit_id).unwrap(), + tree_id: SHA1::from_str(tree).unwrap(), + parent_commit_ids: parents_id + .as_array() + .unwrap() + .iter() + .map(|id| SHA1::from_str(id.as_str().unwrap()).unwrap()) + .collect(), + author: Signature::from_data(author.unwrap().into()).unwrap(), + committer: Signature::from_data(committer.unwrap().into()).unwrap(), + message: message.unwrap(), + } +} + +impl From for Commit { + fn from(value: mega_commit::Model) -> Self { + commit_from_model( + &value.commit_id, + &value.tree, + &value.parents_id, + value.author, + value.committer, + value.content, + ) + } +} + +impl From for Commit { + fn from(value: git_commit::Model) -> Self { + commit_from_model( + &value.commit_id, + &value.tree, + &value.parents_id, + value.author, + value.committer, + value.content, + ) + } +} \ No newline at end of file diff --git a/mercury/src/internal/pack/decode.rs b/mercury/src/internal/pack/decode.rs index 0db2f2053..4b25feb5d 100644 --- a/mercury/src/internal/pack/decode.rs +++ b/mercury/src/internal/pack/decode.rs @@ -701,7 +701,7 @@ mod tests { #[tokio::test] async fn test_pack_check_header() { let res = crate::test_utils::setup_lfs_file().await; - println!("{:?}", res); + println!("{res:?}"); let source = res .get("git-2d187177923cd618a75da6c6db45bb89d92bd504.pack") .unwrap(); @@ -733,7 +733,7 @@ mod tests { assert_eq!(bytes_read, compressed_size); assert_eq!(decompressed_data, data); } - Err(e) => panic!("Decompression failed: {:?}", e), + Err(e) => panic!("Decompression failed: {e:?}"), } } @@ -803,7 +803,7 @@ mod tests { }); if let Err(e) = rt { fs::remove_dir_all(tmp).unwrap(); - panic!("Error: {:?}", e); + panic!("Error: {e:?}"); } } // it will be stuck on dropping `Pack` on Windows if `mem_size` is None, so we need `mimalloc` diff --git a/mercury/src/internal/pack/encode.rs b/mercury/src/internal/pack/encode.rs index c54564f8c..00d701d83 100644 --- a/mercury/src/internal/pack/encode.rs +++ b/mercury/src/internal/pack/encode.rs @@ -570,10 +570,10 @@ mod tests { let value = 16389; let data = encode_offset(value); - println!("{:?}", data); + println!("{data:?}"); let mut reader = Cursor::new(data); let (result, _) = read_offset_encoding(&mut reader).unwrap(); - println!("result: {}", result); + println!("result: {result}" ); assert_eq!(result, value as u64); } }