diff --git a/gix/src/repository/impls.rs b/gix/src/repository/impls.rs index f528cda2860..cbf0d64ef61 100644 --- a/gix/src/repository/impls.rs +++ b/gix/src/repository/impls.rs @@ -1,3 +1,4 @@ +use gix_hash::ObjectId; use gix_object::Exists; use std::ops::DerefMut; @@ -129,6 +130,12 @@ impl gix_object::Write for crate::Repository { impl gix_object::FindHeader for crate::Repository { fn try_header(&self, id: &gix_hash::oid) -> Result, gix_object::find::Error> { + if id == ObjectId::empty_tree(self.object_hash()) { + return Ok(Some(gix_object::Header { + kind: gix_object::Kind::Tree, + size: 0, + })); + } self.objects.try_header(id) } } @@ -139,12 +146,22 @@ impl gix_object::Find for crate::Repository { id: &gix_hash::oid, buffer: &'a mut Vec, ) -> Result>, gix_object::find::Error> { + if id == ObjectId::empty_tree(self.object_hash()) { + buffer.clear(); + return Ok(Some(gix_object::Data { + kind: gix_object::Kind::Tree, + data: &[], + })); + } self.objects.try_find(id, buffer) } } impl gix_object::Exists for crate::Repository { fn exists(&self, id: &gix_hash::oid) -> bool { + if id == ObjectId::empty_tree(self.object_hash()) { + return true; + } self.objects.exists(id) } } diff --git a/gix/src/repository/index.rs b/gix/src/repository/index.rs index 092882d0631..cfd319b985b 100644 --- a/gix/src/repository/index.rs +++ b/gix/src/repository/index.rs @@ -140,7 +140,7 @@ impl crate::Repository { /// Note that this is an expensive operation as it requires recursively traversing the entire tree to unpack it into the index. pub fn index_from_tree(&self, tree: &gix_hash::oid) -> Result { Ok(gix_index::File::from_state( - gix_index::State::from_tree(tree, &self.objects, self.config.protect_options()?).map_err(|err| { + gix_index::State::from_tree(tree, self, self.config.protect_options()?).map_err(|err| { super::index_from_tree::Error::IndexFromTree { id: tree.into(), source: err, diff --git a/gix/src/status/iter/mod.rs b/gix/src/status/iter/mod.rs index f4d58b2a7af..971297f6ee7 100644 --- a/gix/src/status/iter/mod.rs +++ b/gix/src/status/iter/mod.rs @@ -53,7 +53,7 @@ where crate::head::peel::to_object::Error::Unborn { .. }, ), ), - )) => None, + )) => Some(gix_hash::ObjectId::empty_tree(self.repo.object_hash())), Err(err) => return Err(err.into()), }, Some(Some(tree_id)) => Some(tree_id), diff --git a/gix/tests/fixtures/generated-archives/make_status_repos.tar b/gix/tests/fixtures/generated-archives/make_status_repos.tar index e8789ea33c0..7cdb86119dd 100644 Binary files a/gix/tests/fixtures/generated-archives/make_status_repos.tar and b/gix/tests/fixtures/generated-archives/make_status_repos.tar differ diff --git a/gix/tests/fixtures/make_status_repos.sh b/gix/tests/fixtures/make_status_repos.sh index e5828cde625..d006b453de4 100755 --- a/gix/tests/fixtures/make_status_repos.sh +++ b/gix/tests/fixtures/make_status_repos.sh @@ -34,4 +34,10 @@ git init racy-git git init untracked-unborn (cd untracked-unborn touch untracked -) \ No newline at end of file +) + +git init untracked-added +(cd untracked-added + echo content >added + git add added +) diff --git a/gix/tests/gix/repository/object.rs b/gix/tests/gix/repository/object.rs index 87b5a0bf72e..177c75beabe 100644 --- a/gix/tests/gix/repository/object.rs +++ b/gix/tests/gix/repository/object.rs @@ -1,6 +1,32 @@ use crate::util::named_subrepo_opts; use gix_testtools::tempfile; +mod object_database_impl { + use gix_object::{Exists, Find, FindHeader}; + + #[test] + fn empty_tree_is_always_present() -> crate::Result { + let repo = crate::named_subrepo_opts("make_basic_repo.sh", "unborn", gix::open::Options::isolated())?; + let empty_tree = gix::ObjectId::empty_tree(repo.object_hash()); + assert!(repo.exists(&empty_tree)); + assert_eq!( + repo.try_header(&empty_tree)?.expect("tree present"), + gix_object::Header { + kind: gix_object::Kind::Tree, + size: 0 + } + ); + let mut buf = repo.empty_reusable_buffer(); + buf.push(42); + assert_eq!( + repo.try_find(&empty_tree, &mut buf)?.expect("tree present").kind, + gix_object::Kind::Tree + ); + assert_eq!(buf.len(), 0, "the data in the buffer matches the empty tree"); + Ok(()) + } +} + #[cfg(feature = "tree-editor")] mod edit_tree { use crate::util::hex_to_id; diff --git a/gix/tests/gix/status.rs b/gix/tests/gix/status.rs index be32c667382..e8352323a17 100644 --- a/gix/tests/gix/status.rs +++ b/gix/tests/gix/status.rs @@ -113,6 +113,29 @@ mod into_iter { Ok(()) } + #[test] + fn untracked_added() -> crate::Result { + let repo = repo("untracked-added")?; + let mut status = repo.status(gix::progress::Discard)?.into_iter(None)?; + let mut items: Vec<_> = status.by_ref().filter_map(Result::ok).collect(); + items.sort_by(|a, b| a.location().cmp(b.location())); + insta::assert_debug_snapshot!(items, @r#" + [ + TreeIndex( + Addition { + location: "added", + index: 0, + entry_mode: Mode( + FILE, + ), + id: Sha1(d95f3ad14dee633a758d2e331151e950dd13e4ed), + }, + ), + ] + "#); + Ok(()) + } + #[test] fn error_during_tree_traversal_causes_failure() -> crate::Result { let repo = repo("untracked-only")?;