diff --git a/gitoxide-core/src/repository/archive.rs b/gitoxide-core/src/repository/archive.rs index 0f61b387a71..2c41b256f1f 100644 --- a/gitoxide-core/src/repository/archive.rs +++ b/gitoxide-core/src/repository/archive.rs @@ -34,7 +34,7 @@ pub fn stream( .ok_or_else(|| anyhow!("Adding files requires a worktree directory that contains them"))?, )?; for path in add_paths { - stream.add_entry_from_path(&root, &gix::path::realpath(&path)?)?; + stream.add_entry_from_path(&root, &gix::path::realpath(&path)?, repo.object_hash())?; } } for (path, content) in files { diff --git a/gix-archive/tests/archive.rs b/gix-archive/tests/archive.rs index 6a33314d0c2..07020d940e2 100644 --- a/gix-archive/tests/archive.rs +++ b/gix-archive/tests/archive.rs @@ -240,11 +240,12 @@ mod from_tree { .map(|_| ()) }, ); + let hash_kind = gix_testtools::hash_kind_from_env().unwrap_or_default(); stream - .add_entry_from_path(&dir, &dir.join("extra-file"))? - .add_entry_from_path(&dir, &dir.join("extra-exe"))? - .add_entry_from_path(&dir, &dir.join("extra-dir-empty"))? - .add_entry_from_path(&dir, &dir.join("extra-dir").join("symlink-to-extra"))?; + .add_entry_from_path(&dir, &dir.join("extra-file"), hash_kind)? + .add_entry_from_path(&dir, &dir.join("extra-exe"), hash_kind)? + .add_entry_from_path(&dir, &dir.join("extra-dir-empty"), hash_kind)? + .add_entry_from_path(&dir, &dir.join("extra-dir").join("symlink-to-extra"), hash_kind)?; let mut buf = Vec::new(); if format == Format::InternalTransientNonPersistable { diff --git a/gix-odb/src/store_impls/dynamic/handle.rs b/gix-odb/src/store_impls/dynamic/handle.rs index 832079a1623..46fbc2f1a43 100644 --- a/gix-odb/src/store_impls/dynamic/handle.rs +++ b/gix-odb/src/store_impls/dynamic/handle.rs @@ -349,7 +349,7 @@ impl TryFrom<&super::Store> for super::Store { &mut s.replacements(), crate::store::init::Options { slots: crate::store::init::Slots::Given(s.files.len().try_into().expect("BUG: too many slots")), - object_hash: Default::default(), + object_hash: s.object_hash, use_multi_pack_index: false, alloc_limit_bytes: s.alloc_limit_bytes, current_dir: s.current_dir.clone().into(), diff --git a/gix-worktree-stream/Cargo.toml b/gix-worktree-stream/Cargo.toml index ca7ec85e7cc..1371aa9d112 100644 --- a/gix-worktree-stream/Cargo.toml +++ b/gix-worktree-stream/Cargo.toml @@ -17,6 +17,8 @@ doctest = false [features] ## Enable support for the SHA-1 hash by forwarding the feature to dependencies. sha1 = ["gix-filter/sha1", "gix-hash/sha1", "gix-object/sha1", "gix-traverse/sha1"] +## Enable support for the SHA-256 hash by forwarding the feature to dependencies. +sha256 = ["gix-hash/sha256"] [dependencies] gix-features = { version = "^0.47.0", path = "../gix-features", features = ["progress", "io-pipe"] } @@ -33,6 +35,7 @@ parking_lot = "0.12.4" [dev-dependencies] gix-testtools = { path = "../tests/tools" } +gix-hash = { version = "^0.24.0", path = "../gix-hash", features = ["sha1", "sha256"] } gix-odb = { path = "../gix-odb" } gix-worktree = { path = "../gix-worktree", default-features = false, features = ["attributes"] } diff --git a/gix-worktree-stream/src/lib.rs b/gix-worktree-stream/src/lib.rs index 743df783e9d..749b8d5c997 100644 --- a/gix-worktree-stream/src/lib.rs +++ b/gix-worktree-stream/src/lib.rs @@ -125,11 +125,16 @@ impl Stream { /// /// Note that the created entries will always have a null SHA1, and that we access this path /// to determine its type, and will access it again when it is requested. - pub fn add_entry_from_path(&mut self, root: &Path, path: &Path) -> std::io::Result<&mut Self> { + pub fn add_entry_from_path( + &mut self, + root: &Path, + path: &Path, + hash_kind: gix_hash::Kind, + ) -> std::io::Result<&mut Self> { let rela_path = path.strip_prefix(root).map_err(std::io::Error::other)?; let meta = path.symlink_metadata()?; let relative_path = gix_path::to_unix_separators_on_windows(gix_path::into_bstr(rela_path)).into_owned(); - let id = gix_hash::ObjectId::null(gix_hash::Kind::Sha1); + let id = hash_kind.null(); let entry = if meta.is_symlink() { let content = std::fs::read_link(path)?; diff --git a/gix-worktree-stream/src/protocol.rs b/gix-worktree-stream/src/protocol.rs index 7e8e1f8b44c..ff4d94c402a 100644 --- a/gix-worktree-stream/src/protocol.rs +++ b/gix-worktree-stream/src/protocol.rs @@ -91,6 +91,8 @@ pub(crate) fn write_stream( fn byte_to_hash(b: u8) -> gix_hash::Kind { match b { 0 => gix_hash::Kind::Sha1, + #[cfg(feature = "sha256")] + 1 => gix_hash::Kind::Sha256, _ => unreachable!("BUG: we control the protocol"), } } @@ -111,6 +113,8 @@ fn byte_to_mode(b: u8) -> gix_object::tree::EntryMode { fn hash_to_byte(h: gix_hash::Kind) -> u8 { match h { gix_hash::Kind::Sha1 => 0, + #[cfg(feature = "sha256")] + gix_hash::Kind::Sha256 => 1, _ => unreachable!("BUG: not implemented for hash kind {h}"), } } diff --git a/gix-worktree-stream/tests/stream.rs b/gix-worktree-stream/tests/stream.rs index 5f21584e248..cc5c7a7eedc 100644 --- a/gix-worktree-stream/tests/stream.rs +++ b/gix-worktree-stream/tests/stream.rs @@ -1,6 +1,10 @@ /// Convert a hexadecimal hash into its corresponding `ObjectId` or _panic_. -fn hex_to_id(hex: &str) -> gix_hash::ObjectId { - gix_hash::ObjectId::from_hex(hex.as_bytes()).expect("40 bytes hex") +fn hex_to_id(hex_sha1: &str, hex_sha256: &str) -> gix_hash::ObjectId { + match gix_testtools::hash_kind_from_env().unwrap_or_default() { + gix_hash::Kind::Sha1 => gix_hash::ObjectId::from_hex(hex_sha1.as_bytes()).expect("40 bytes hex"), + gix_hash::Kind::Sha256 => gix_hash::ObjectId::from_hex(hex_sha256.as_bytes()).expect("64 bytes hex"), + _ => unimplemented!(), + } } mod from_tree { @@ -35,7 +39,7 @@ mod from_tree { #[test] fn can_receive_err_if_root_is_not_found() { let mut stream = gix_worktree_stream::from_tree( - gix_hash::Kind::Sha1.null(), + gix_testtools::hash_kind_from_env().unwrap_or_default().null(), FailObjectRetrieval, mutating_pipeline(false), |_, _, _| -> Result<_, Infallible> { unreachable!("must not be called") }, @@ -58,11 +62,6 @@ mod from_tree { Ok(()) } - #[cfg(target_pointer_width = "64")] - const EXPECTED_BUFFER_LENGTH: usize = 320302; - #[cfg(target_pointer_width = "32")] - const EXPECTED_BUFFER_LENGTH: usize = 320198; - #[test] fn will_provide_all_information_and_respect_export_ignore() -> gix_testtools::Result { let (dir, head_tree, odb, mut cache) = basic()?; @@ -77,12 +76,13 @@ mod from_tree { .map(|_| ()) }, ); + let hash_kind = gix_testtools::hash_kind_from_env().unwrap_or_default(); stream - .add_entry_from_path(&dir, &dir.join("extra-file"))? - .add_entry_from_path(&dir, &dir.join("extra-bigfile"))? - .add_entry_from_path(&dir, &dir.join("extra-exe"))? - .add_entry_from_path(&dir, &dir.join("extra-dir-empty"))? - .add_entry_from_path(&dir, &dir.join("extra-dir").join("symlink-to-extra"))?; + .add_entry_from_path(&dir, &dir.join("extra-file"), hash_kind)? + .add_entry_from_path(&dir, &dir.join("extra-bigfile"), hash_kind)? + .add_entry_from_path(&dir, &dir.join("extra-exe"), hash_kind)? + .add_entry_from_path(&dir, &dir.join("extra-dir-empty"), hash_kind)? + .add_entry_from_path(&dir, &dir.join("extra-dir").join("symlink-to-extra"), hash_kind)?; let tee_read = TeeToMemory { read: stream.into_read(), @@ -121,53 +121,69 @@ mod from_tree { ( ".gitattributes".into(), EntryKind::Blob, - hex_to_id("45c160c35c17ad264b96431cceb9793160396e99") + hex_to_id( + "45c160c35c17ad264b96431cceb9793160396e99", + "e34ae8e2c517230c6c613202a955b5f2095567830de5c3bb7081d72f1af393dd" + ) ), ( "a".into(), EntryKind::Blob, - hex_to_id("45b983be36b73c0788dc9cbcb76cbb80fc7bb057") + hex_to_id( + "45b983be36b73c0788dc9cbcb76cbb80fc7bb057", + "96c18f0297e38d01f4b2dacddea4259aea6b2961eb0822bd2c0c3f6029030045" + ) ), ( "bigfile".into(), EntryKind::Blob, - hex_to_id("4995fde49ed64e043977e22539f66a0d372dd129") + hex_to_id( + "4995fde49ed64e043977e22539f66a0d372dd129", + "527a170c0c29520af58bf61a383c4e99a7a7c63c0475cbb630cf98d38d6c0dce" + ) ), ( "symlink-to-a".into(), EntryKind::Link, - hex_to_id("2e65efe2a145dda7ee51d1741299f848e5bf752e") + hex_to_id( + "2e65efe2a145dda7ee51d1741299f848e5bf752e", + "eb337bcee2061c5313c9a1392116b6c76039e9e30d71467ae359b36277e17dc7" + ) ), ( "dir/.gitattributes".into(), EntryKind::Blob, - hex_to_id("81b9a375276405703e05be6cecf0fc1c8b8eed64") + hex_to_id( + "81b9a375276405703e05be6cecf0fc1c8b8eed64", + "4b74474eca46ad7e1807afe8855e7a511d132b314d3d0f481bb55a331b249fb7" + ) ), ( "dir/b".into(), EntryKind::Blob, - hex_to_id("ab4a98190cf776b43cb0fe57cef231fb93fd07e6") + hex_to_id( + "ab4a98190cf776b43cb0fe57cef231fb93fd07e6", + "cf84f9ae227fa5c481dacd97f6d6506de727dabd30377c6907f623ef1192637a" + ) ), ( "dir/subdir/exe".into(), EntryKind::BlobExecutable, - hex_to_id("e69de29bb2d1d6434b8b29ae775ad8c2e48c5391") + hex_to_id( + "e69de29bb2d1d6434b8b29ae775ad8c2e48c5391", + "473a0f4c3be8a93681a267e3b1e9a7dcda1185436fe141f7749120a303721813" + ) ), ( "dir/subdir/streamed".into(), EntryKind::Blob, - hex_to_id("08991f58f4de5d85b61c0f87f3ac053c79d0e739") - ), - ( - "extra-file".into(), - EntryKind::Blob, - hex_to_id("0000000000000000000000000000000000000000") - ), - ( - "extra-bigfile".into(), - EntryKind::Blob, - hex_to_id("0000000000000000000000000000000000000000") + hex_to_id( + "08991f58f4de5d85b61c0f87f3ac053c79d0e739", + "8c13ad7df9686daf00357f34700922485802cb0be33e684ec5171f7d0d8a84fd" + ) ), + ("extra-file".into(), EntryKind::Blob, hash_kind.null()), + ("extra-bigfile".into(), EntryKind::Blob, hash_kind.null()), ( "extra-exe".into(), if cfg!(windows) { @@ -175,23 +191,29 @@ mod from_tree { } else { EntryKind::BlobExecutable }, - hex_to_id("0000000000000000000000000000000000000000") - ), - ( - "extra-dir-empty".into(), - EntryKind::Tree, - hex_to_id("0000000000000000000000000000000000000000") + hash_kind.null() ), - ( - "extra-dir/symlink-to-extra".into(), - EntryKind::Link, - hex_to_id("0000000000000000000000000000000000000000") - ) + ("extra-dir-empty".into(), EntryKind::Tree, hash_kind.null()), + ("extra-dir/symlink-to-extra".into(), EntryKind::Link, hash_kind.null()) ] ); + + #[cfg(target_pointer_width = "64")] + let expected_buffer_length: usize = match gix_testtools::hash_kind_from_env().unwrap_or_default() { + gix_hash::Kind::Sha1 => 320302, + gix_hash::Kind::Sha256 => 320458, + _ => unimplemented!(), + }; + #[cfg(target_pointer_width = "32")] + let expected_buffer_length: usize = match gix_testtools::hash_kind_from_env().unwrap_or_default() { + gix_hash::Kind::Sha1 => 320198, + gix_hash::Kind::Sha256 => todo!("let the test fail on CI and add the value here"), + _ => unimplemented!(), + }; + assert_eq!( copy.lock().len(), - EXPECTED_BUFFER_LENGTH, + expected_buffer_length, "keep track of file size changes of the streaming format" ); @@ -237,7 +259,14 @@ mod from_tree { let hex = std::fs::read(dir.join("head.hex"))?; gix_hash::ObjectId::from_hex(hex.trim())? }; - let odb = gix_odb::at(dir.join(".git").join("objects"))?; + let odb = gix_odb::at_opts( + dir.join(".git").join("objects"), + Vec::new(), + gix_odb::store::init::Options { + object_hash: gix_testtools::hash_kind_from_env().unwrap_or_default(), + ..Default::default() + }, + )?; let mut collection = Default::default(); let mut buf = Default::default(); diff --git a/justfile b/justfile index 0c247e8a4d9..8006ab9d775 100755 --- a/justfile +++ b/justfile @@ -240,6 +240,8 @@ unit-tests: cargo nextest run -p gix-blame --no-fail-fast env GIX_TEST_FIXTURE_HASH=sha1 cargo nextest run -p gix-refspec --no-fail-fast env GIX_TEST_FIXTURE_HASH=sha256 cargo nextest run -p gix-refspec --no-fail-fast + env GIX_TEST_FIXTURE_HASH=sha1 cargo nextest run -p gix-worktree-stream --no-fail-fast + env GIX_TEST_FIXTURE_HASH=sha256 cargo nextest run -p gix-worktree-stream --features sha256 --no-fail-fast cargo nextest run -p gix --no-default-features --no-fail-fast cargo nextest run -p gix --no-default-features --features basic,comfort,max-performance-safe --no-fail-fast cargo nextest run -p gix --no-default-features --features basic,extras,comfort,need-more-recent-msrv --no-fail-fast