blob: 2f16b8c6cf6eaf012a21954c60a195348a31302a [file] [log] [blame]
use crate::{
ext::{ObjectIdExt, ReferenceExt},
head::Kind,
Head,
};
mod error {
use crate::{object, reference};
/// The error returned by [`Head::peel_to_id_in_place()`](super::Head::try_peel_to_id_in_place())
/// and [`Head::into_fully_peeled_id()`](super::Head::try_into_peeled_id()).
#[derive(Debug, thiserror::Error)]
#[allow(missing_docs)]
pub enum Error {
#[error(transparent)]
FindExistingObject(#[from] object::find::existing::Error),
#[error(transparent)]
PeelReference(#[from] reference::peel::Error),
}
}
pub use error::Error;
///
#[allow(clippy::empty_docs)]
pub mod into_id {
use crate::object;
/// The error returned by [`Head::into_peeled_id()`](super::Head::into_peeled_id()).
#[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),
}
}
///
#[allow(clippy::empty_docs)]
pub mod to_commit {
use crate::object;
/// 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),
}
}
///
#[allow(clippy::empty_docs)]
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 },
}
}
impl<'repo> Head<'repo> {
/// 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.
pub fn into_peeled_id(mut self) -> Result<crate::Id<'repo>, into_id::Error> {
self.try_peel_to_id_in_place()?;
self.id().ok_or_else(|| match self.kind {
Kind::Symbolic(gix_ref::Reference { name, .. }) | Kind::Unborn(name) => into_id::Error::Unborn { name },
Kind::Detached { .. } => unreachable!("id can be returned after peeling"),
})
}
/// 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.
///
/// 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.
pub fn try_into_peeled_id(mut self) -> Result<Option<crate::Id<'repo>>, Error> {
self.try_peel_to_id_in_place()
}
/// 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, and return that object id.
///
/// Returns `Ok(None)` if the head is unborn.
///
/// 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.
pub fn try_peel_to_id_in_place(&mut self) -> Result<Option<crate::Id<'repo>>, Error> {
Ok(Some(match &mut self.kind {
Kind::Unborn(_name) => return Ok(None),
Kind::Detached {
peeled: Some(peeled), ..
} => (*peeled).attach(self.repo),
Kind::Detached { peeled: None, target } => {
let id = target.attach(self.repo);
if id.header()?.kind() == gix_object::Kind::Commit {
id
} else {
match id.object()?.peel_tags_to_end() {
Ok(obj) => {
self.kind = Kind::Detached {
peeled: Some(obj.id),
target: *target,
};
obj.id()
}
Err(err) => return Err(err.into()),
}
}
}
Kind::Symbolic(r) => {
let mut nr = r.clone().attach(self.repo);
let peeled = nr.peel_to_id_in_place();
*r = nr.detach();
peeled?
}
}))
}
/// 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_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_object::Error::Unborn {
name: self.referent_name().expect("unborn").to_owned(),
})?;
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()?)
}
}