Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 31 additions & 1 deletion gix/src/repository/object.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,14 @@ impl crate::Repository {
repo: self,
});
}
if id == ObjectId::empty_blob(self.object_hash()) {
return Ok(Object {
id,
kind: gix_object::Kind::Blob,
data: Vec::new(),
repo: self,
});
}
let mut buf = self.free_buf();
let kind = self.objects.find(&id, &mut buf)?.kind;
Ok(Object::from_data(id, kind, buf, self))
Expand Down Expand Up @@ -86,6 +94,8 @@ impl crate::Repository {
/// Obtain information about an object without fully decoding it, or fail if the object doesn't exist.
///
/// Note that despite being cheaper than [`Self::find_object()`], there is still some effort traversing delta-chains.
/// Also note that for empty trees and blobs, it will always report it to exist in loose objects, even if they don't
/// exist or if they exist in a pack.
#[doc(alias = "read_header", alias = "git2")]
pub fn find_header(&self, id: impl Into<ObjectId>) -> Result<gix_odb::find::Header, object::find::existing::Error> {
let id = id.into();
Expand All @@ -95,6 +105,12 @@ impl crate::Repository {
size: 0,
});
}
if id == ObjectId::empty_blob(self.object_hash()) {
return Ok(gix_odb::find::Header::Loose {
kind: gix_object::Kind::Blob,
size: 0,
});
}
self.objects.header(id)
}

Expand All @@ -109,7 +125,7 @@ impl crate::Repository {
#[doc(alias = "exists", alias = "git2")]
pub fn has_object(&self, id: impl AsRef<gix_hash::oid>) -> bool {
let id = id.as_ref();
if id.to_owned().is_empty_tree() {
if id.to_owned().is_empty_tree() || id.to_owned().is_empty_blob() {
true
} else {
self.objects.exists(id)
Expand All @@ -130,6 +146,12 @@ impl crate::Repository {
size: 0,
}));
}
if id == ObjectId::empty_blob(self.object_hash()) {
return Ok(Some(gix_odb::find::Header::Loose {
kind: gix_object::Kind::Blob,
size: 0,
}));
}
self.objects.try_header(&id).map_err(Into::into)
}

Expand All @@ -144,6 +166,14 @@ impl crate::Repository {
repo: self,
}));
}
if id == ObjectId::empty_blob(self.object_hash()) {
return Ok(Some(Object {
id,
kind: gix_object::Kind::Blob,
data: Vec::new(),
repo: self,
}));
}

let mut buf = self.free_buf();
match self.objects.try_find(&id, &mut buf)? {
Expand Down
72 changes: 72 additions & 0 deletions gix/tests/gix/repository/object.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use gix_odb::Header;
use gix_pack::Find;
use gix_testtools::tempfile;

use crate::util::named_subrepo_opts;
Expand Down Expand Up @@ -507,6 +509,76 @@ mod find {
);
Ok(())
}

#[test]
fn empty_blob_can_always_be_found() -> crate::Result {
let repo = basic_repo()?;
let empty_blob = gix::hash::ObjectId::empty_blob(repo.object_hash());
assert_eq!(repo.find_object(empty_blob)?.into_blob().data.len(), 0);
assert!(repo.has_object(empty_blob));
assert_eq!(
repo.find_header(empty_blob)?,
gix_odb::find::Header::Loose {
kind: gix_object::Kind::Blob,
size: 0,
},
"empty blob is considered a loose object"
);
assert_eq!(
repo.try_find_object(empty_blob)?
.expect("present")
.into_blob()
.data
.len(),
0
);
assert_eq!(
repo.try_find_header(empty_blob)?,
Some(gix_odb::find::Header::Loose {
kind: gix_object::Kind::Blob,
size: 0,
}),
"empty blob is considered a loose object"
);
Ok(())
}
}

#[test]
fn empty_objects_are_always_present_but_not_in_plumbing() -> crate::Result {
let repo = empty_bare_in_memory_repo()?;
let empty_blob_id = gix::hash::ObjectId::empty_blob(repo.object_hash());

assert!(
repo.has_object(empty_blob_id),
"empty object is always present even if it's not"
);
assert!(!repo.objects.contains(&empty_blob_id));

let header = repo.find_header(empty_blob_id)?;
assert_eq!(header.kind(), gix_object::Kind::Blob);
assert_eq!(header.size(), 0);
assert_eq!(repo.objects.try_header(&empty_blob_id)?, None);

let header = repo.try_find_header(empty_blob_id)?.expect("should find header");
assert_eq!(header.kind(), gix_object::Kind::Blob);
assert_eq!(header.size(), 0);

let obj = repo.find_object(empty_blob_id)?;
assert_eq!(obj.kind, gix_object::Kind::Blob);
assert_eq!(obj.data.len(), 0);

let mut buf = Vec::new();
assert_eq!(repo.objects.try_find(&empty_blob_id, &mut buf)?, None);

let obj = repo.try_find_object(empty_blob_id)?.expect("should find object");
assert_eq!(obj.kind, gix_object::Kind::Blob);
assert_eq!(obj.data.len(), 0);

let blob = obj.try_into_blob()?;
assert_eq!(blob.data.len(), 0);

Ok(())
}

mod tag {
Expand Down
Loading