Skip to content

Commit

Permalink
feat: add Head::try_into_peeled_object() and `Head::peel_to_object_…
Browse files Browse the repository at this point in the history
…in_place()`

This makes it easier to peel to a specific object type, after
all tags have been followed, without having to assume an intermediate
commit.
  • Loading branch information
Byron committed Oct 13, 2023
1 parent 4e6a4e6 commit 117357e
Show file tree
Hide file tree
Showing 4 changed files with 39 additions and 14 deletions.
45 changes: 35 additions & 10 deletions gix/src/head/peel.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,21 +42,32 @@ pub mod into_id {
pub mod to_commit {
use crate::object;

/// The error returned by [`Head::peel_to_commit_in_place()`][super::Head::peel_to_commit_in_place()].
/// The error returned by [`Head::peel_to_commit_in_place()`](super::Head::peel_to_commit_in_place()).
#[derive(Debug, thiserror::Error)]
#[allow(missing_docs)]
pub enum Error {
#[error(transparent)]
PeelToObject(#[from] super::to_object::Error),
#[error(transparent)]
ObjectKind(#[from] object::try_into::Error),
}
}

///
pub mod to_object {
/// The error returned by [`Head::peel_to_object_in_place()`](super::Head::peel_to_object_in_place()).
#[derive(Debug, thiserror::Error)]
#[allow(missing_docs)]
pub enum Error {
#[error(transparent)]
Peel(#[from] super::Error),
#[error("Branch '{name}' does not have any commits")]
Unborn { name: gix_ref::FullName },
#[error(transparent)]
ObjectKind(#[from] object::try_into::Error),
}
}

impl<'repo> Head<'repo> {
/// Peel this instance and consume to make obtaining its final target id possible, while returning an error on unborn heads.
/// Peel this instance and consume it to make obtaining its final target id possible, while returning an error on unborn heads.
///
/// The final target is obtained by following symbolic references and peeling tags to their final destination, which
/// typically is a commit, but can be any object.
Expand All @@ -68,6 +79,14 @@ impl<'repo> Head<'repo> {
})
}

/// Peel this instance and consume it to make obtaining its final target object possible, while returning an error on unborn heads.
///
/// The final target is obtained by following symbolic references and peeling tags to their final destination, which
/// typically is a commit, but can be any object as well.
pub fn into_peeled_object(mut self) -> Result<crate::Object<'repo>, to_object::Error> {
self.peel_to_object_in_place()
}

/// Consume this instance and transform it into the final object that it points to, or `Ok(None)` if the `HEAD`
/// reference is yet to be born.
///
Expand Down Expand Up @@ -120,15 +139,21 @@ impl<'repo> Head<'repo> {
/// more object to follow, transform the id into a commit if possible and return that.
///
/// Returns an error if the head is unborn or if it doesn't point to a commit.
pub fn peel_to_commit_in_place(&mut self) -> Result<crate::Commit<'repo>, to_commit::Error> {
pub fn peel_to_object_in_place(&mut self) -> Result<crate::Object<'repo>, to_object::Error> {
let id = self
.try_peel_to_id_in_place()?
.ok_or_else(|| to_commit::Error::Unborn {
.ok_or_else(|| to_object::Error::Unborn {
name: self.referent_name().expect("unborn").to_owned(),
})?;
Ok(id
.object()
.map_err(|err| to_commit::Error::Peel(Error::FindExistingObject(err)))?
.try_into_commit()?)
id.object()
.map_err(|err| to_object::Error::Peel(Error::FindExistingObject(err)))
}

/// Follow the symbolic reference of this head until its target object and peel it by following tag objects until there is no
/// more object to follow, transform the id into a commit if possible and return that.
///
/// Returns an error if the head is unborn or if it doesn't point to a commit.
pub fn peel_to_commit_in_place(&mut self) -> Result<crate::Commit<'repo>, to_commit::Error> {
Ok(self.peel_to_object_in_place()?.try_into_commit()?)
}
}
4 changes: 1 addition & 3 deletions gix/src/reference/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,9 +67,7 @@ pub mod head_tree_id {
#[allow(missing_docs)]
pub enum Error {
#[error(transparent)]
Head(#[from] crate::reference::find::existing::Error),
#[error(transparent)]
PeelToCommit(#[from] crate::head::peel::to_commit::Error),
HeadCommit(#[from] crate::reference::head_commit::Error),
#[error(transparent)]
DecodeCommit(#[from] gix_object::decode::Error),
}
Expand Down
2 changes: 1 addition & 1 deletion gix/src/repository/reference.rs
Original file line number Diff line number Diff line change
Expand Up @@ -216,7 +216,7 @@ impl crate::Repository {
/// is freshly initialized and doesn't have any commits yet. It could also fail if the
/// head does not point to a commit.
pub fn head_tree_id(&self) -> Result<crate::Id<'_>, reference::head_tree_id::Error> {
Ok(self.head()?.peel_to_commit_in_place()?.tree_id()?)
Ok(self.head_commit()?.tree_id()?)
}

/// Find the reference with the given partial or full `name`, like `main`, `HEAD`, `heads/branch` or `origin/other`,
Expand Down
2 changes: 2 additions & 0 deletions gix/tests/head/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,13 @@ mod peel {
for name in ["detached", "symbolic", "tag-detached", "tag-symbolic"] {
let repo = named_subrepo_opts("make_head_repos.sh", name, gix::open::Options::isolated())?;
assert_eq!(repo.head()?.into_peeled_id()?, expected_commit);
assert_eq!(repo.head()?.into_peeled_object()?.id, expected_commit);
assert_eq!(repo.head_id()?, expected_commit);
let commit = repo.head_commit()?;
assert_eq!(commit.id, expected_commit);
assert_eq!(repo.head_tree_id()?, commit.tree_id()?);
assert_eq!(repo.head()?.try_into_peeled_id()?.expect("born"), expected_commit);
assert_eq!(repo.head()?.peel_to_object_in_place()?.id, expected_commit);
assert_eq!(repo.head()?.try_peel_to_id_in_place()?.expect("born"), expected_commit);
}
Ok(())
Expand Down

0 comments on commit 117357e

Please sign in to comment.