From 0f20e0a542b168c212b72d5823e4a7e0b1d0095c Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 13 Sep 2025 18:53:45 +0200 Subject: [PATCH 1/2] feat: Add `oid::is_empty_tree()`` and `oid::is_empty_blob()`. --- gix-hash/src/oid.rs | 28 ++++++++++++++++++++++++++ gix-hash/tests/hash/oid.rs | 41 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 69 insertions(+) diff --git a/gix-hash/src/oid.rs b/gix-hash/src/oid.rs index 7b35c310d3c..1cde8755eb2 100644 --- a/gix-hash/src/oid.rs +++ b/gix-hash/src/oid.rs @@ -148,6 +148,22 @@ impl oid { Kind::Sha1 => &self.bytes == oid::null_sha1().as_bytes(), } } + + /// Returns `true` if this hash is equal to an empty blob. + #[inline] + pub fn is_empty_blob(&self) -> bool { + match self.kind() { + Kind::Sha1 => &self.bytes == oid::empty_blob_sha1().as_bytes(), + } + } + + /// Returns `true` if this hash is equal to an empty tree. + #[inline] + pub fn is_empty_tree(&self) -> bool { + match self.kind() { + Kind::Sha1 => &self.bytes == oid::empty_tree_sha1().as_bytes(), + } + } } /// Sha1 specific methods @@ -175,6 +191,18 @@ impl oid { pub(crate) fn null_sha1() -> &'static Self { oid::from_bytes([0u8; SIZE_OF_SHA1_DIGEST].as_ref()) } + + /// Returns an oid representing the hash of an empty blob. + #[inline] + pub(crate) fn empty_blob_sha1() -> &'static Self { + oid::from_bytes(b"\xe6\x9d\xe2\x9b\xb2\xd1\xd6\x43\x4b\x8b\x29\xae\x77\x5a\xd8\xc2\xe4\x8c\x53\x91") + } + + /// Returns an oid representing the hash of an empty tree. + #[inline] + pub(crate) fn empty_tree_sha1() -> &'static Self { + oid::from_bytes(b"\x4b\x82\x5d\xc6\x42\xcb\x6e\xb9\xa0\x60\xe5\x4b\xf8\xd6\x92\x88\xfb\xee\x49\x04") + } } impl AsRef for &oid { diff --git a/gix-hash/tests/hash/oid.rs b/gix-hash/tests/hash/oid.rs index 7b46e559492..b89eed7bc93 100644 --- a/gix-hash/tests/hash/oid.rs +++ b/gix-hash/tests/hash/oid.rs @@ -19,3 +19,44 @@ fn is_null() { assert!(gix_hash::Kind::Sha1.null().is_null()); assert!(gix_hash::Kind::Sha1.null().as_ref().is_null()); } + +#[test] +fn is_empty_blob() { + // Test with ObjectId::empty_blob + let empty_blob = gix_hash::ObjectId::empty_blob(gix_hash::Kind::Sha1); + assert!(empty_blob.is_empty_blob()); + assert!(empty_blob.as_ref().is_empty_blob()); + + // Test that non-empty blob hash returns false + let non_empty = gix_hash::Kind::Sha1.null(); + assert!(!non_empty.is_empty_blob()); + assert!(!non_empty.as_ref().is_empty_blob()); +} + +#[test] +fn is_empty_tree() { + // Test with ObjectId::empty_tree + let empty_tree = gix_hash::ObjectId::empty_tree(gix_hash::Kind::Sha1); + assert!(empty_tree.is_empty_tree()); + assert!(empty_tree.as_ref().is_empty_tree()); + + // Test that non-empty tree hash returns false + let non_empty = gix_hash::Kind::Sha1.null(); + assert!(!non_empty.is_empty_tree()); + assert!(!non_empty.as_ref().is_empty_tree()); +} + +#[test] +fn oid_methods_are_consistent_with_objectid() { + // Verify that the oid methods return the same results as ObjectId methods + let empty_blob = gix_hash::ObjectId::empty_blob(gix_hash::Kind::Sha1); + let empty_tree = gix_hash::ObjectId::empty_tree(gix_hash::Kind::Sha1); + + // Check that ObjectId and oid versions give same results + assert_eq!(empty_blob.is_empty_blob(), empty_blob.as_ref().is_empty_blob()); + assert_eq!(empty_tree.is_empty_tree(), empty_tree.as_ref().is_empty_tree()); + + // Check cross-validation (empty blob is not empty tree and vice versa) + assert!(!empty_blob.as_ref().is_empty_tree()); + assert!(!empty_tree.as_ref().is_empty_blob()); +} From 58e2d18cd0684f2da51f94eb51e4402165e63efa Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Sat, 13 Sep 2025 18:54:42 +0200 Subject: [PATCH 2/2] refactor --- gix-hash/tests/hash/oid.rs | 23 ++--------------------- 1 file changed, 2 insertions(+), 21 deletions(-) diff --git a/gix-hash/tests/hash/oid.rs b/gix-hash/tests/hash/oid.rs index b89eed7bc93..e1e23e7840a 100644 --- a/gix-hash/tests/hash/oid.rs +++ b/gix-hash/tests/hash/oid.rs @@ -22,12 +22,10 @@ fn is_null() { #[test] fn is_empty_blob() { - // Test with ObjectId::empty_blob let empty_blob = gix_hash::ObjectId::empty_blob(gix_hash::Kind::Sha1); assert!(empty_blob.is_empty_blob()); assert!(empty_blob.as_ref().is_empty_blob()); - - // Test that non-empty blob hash returns false + let non_empty = gix_hash::Kind::Sha1.null(); assert!(!non_empty.is_empty_blob()); assert!(!non_empty.as_ref().is_empty_blob()); @@ -35,28 +33,11 @@ fn is_empty_blob() { #[test] fn is_empty_tree() { - // Test with ObjectId::empty_tree let empty_tree = gix_hash::ObjectId::empty_tree(gix_hash::Kind::Sha1); assert!(empty_tree.is_empty_tree()); assert!(empty_tree.as_ref().is_empty_tree()); - - // Test that non-empty tree hash returns false + let non_empty = gix_hash::Kind::Sha1.null(); assert!(!non_empty.is_empty_tree()); assert!(!non_empty.as_ref().is_empty_tree()); } - -#[test] -fn oid_methods_are_consistent_with_objectid() { - // Verify that the oid methods return the same results as ObjectId methods - let empty_blob = gix_hash::ObjectId::empty_blob(gix_hash::Kind::Sha1); - let empty_tree = gix_hash::ObjectId::empty_tree(gix_hash::Kind::Sha1); - - // Check that ObjectId and oid versions give same results - assert_eq!(empty_blob.is_empty_blob(), empty_blob.as_ref().is_empty_blob()); - assert_eq!(empty_tree.is_empty_tree(), empty_tree.as_ref().is_empty_tree()); - - // Check cross-validation (empty blob is not empty tree and vice versa) - assert!(!empty_blob.as_ref().is_empty_tree()); - assert!(!empty_tree.as_ref().is_empty_blob()); -}