diff --git a/gix/src/object/tree/editor.rs b/gix/src/object/tree/editor.rs index d7a42fcd9bb..48fd57167df 100644 --- a/gix/src/object/tree/editor.rs +++ b/gix/src/object/tree/editor.rs @@ -263,7 +263,7 @@ fn write_cursor<'repo>(cursor: &mut Cursor<'_, 'repo>) -> Result, writ id: entry.oid, source: err, })?; - if !cursor.repo.has_object(entry.oid) { + if !entry.mode.is_commit() && !cursor.repo.has_object(entry.oid) { return Err(write::Error::MissingObject { filename: entry.filename.clone(), kind: entry.mode.into(), diff --git a/gix/src/reference/errors.rs b/gix/src/reference/errors.rs index cacfda5aeab..efd2b1721ee 100644 --- a/gix/src/reference/errors.rs +++ b/gix/src/reference/errors.rs @@ -106,6 +106,19 @@ pub mod head_tree_id { } } +/// +pub mod head_tree { + /// The error returned by [`Repository::head_tree`(…)](crate::Repository::head_tree()). + #[derive(Debug, thiserror::Error)] + #[allow(missing_docs)] + pub enum Error { + #[error(transparent)] + HeadCommit(#[from] crate::reference::head_commit::Error), + #[error(transparent)] + CommitTree(#[from] crate::object::commit::Error), + } +} + /// pub mod find { /// diff --git a/gix/src/reference/mod.rs b/gix/src/reference/mod.rs index 68308f433ba..924f3f6105f 100644 --- a/gix/src/reference/mod.rs +++ b/gix/src/reference/mod.rs @@ -10,7 +10,7 @@ pub mod iter; pub mod remote; mod errors; -pub use errors::{edit, find, follow, head_commit, head_id, head_tree_id, peel}; +pub use errors::{edit, find, follow, head_commit, head_id, head_tree, head_tree_id, peel}; use crate::ext::ObjectIdExt; diff --git a/gix/src/repository/reference.rs b/gix/src/repository/reference.rs index 90db4032ceb..a0c3b6ffb0c 100644 --- a/gix/src/repository/reference.rs +++ b/gix/src/repository/reference.rs @@ -215,6 +215,16 @@ impl crate::Repository { Ok(self.head_commit()?.tree_id()?) } + /// Return the tree object the `HEAD^{tree}` reference currently points to after peeling it fully, + /// following symbolic references and tags until a tree is found. + /// + /// Note that this may fail for various reasons, most notably because the repository + /// is freshly initialized and doesn't have any commits yet. It could also fail if the + /// head does not point to a tree, unlikely but possible. + pub fn head_tree(&self) -> Result, reference::head_tree::Error> { + Ok(self.head_commit()?.tree()?) + } + /// Find the reference with the given partial or full `name`, like `main`, `HEAD`, `heads/branch` or `origin/other`, /// or return an error if it wasn't found. /// diff --git a/gix/tests/gix/repository/object.rs b/gix/tests/gix/repository/object.rs index a4979900073..969784e7b00 100644 --- a/gix/tests/gix/repository/object.rs +++ b/gix/tests/gix/repository/object.rs @@ -118,6 +118,20 @@ mod edit_tree { Ok(()) } + #[test] + fn submodules_are_not_checked_for_existence() -> crate::Result { + let repo = crate::named_subrepo_opts("make_submodules.sh", "with-submodules", gix::open::Options::isolated())? + .with_object_memory(); + let mut editor = repo.head_tree()?.edit()?; + let actual = editor.write()?; + assert_eq!( + actual, + repo.head_tree_id()?, + "Nothing changed, but it did validate the root tree that it would want to write" + ); + Ok(()) + } + #[test] fn missing_objects_and_illformed_path_components_trigger_error() -> crate::Result { let (repo, _tmp) = crate::repo_rw("make_packed_and_loose.sh")?;