summaryrefslogtreecommitdiffstats
path: root/vendor/gix/src/head
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-04 12:41:41 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-04 12:41:41 +0000
commit10ee2acdd26a7f1298c6f6d6b7af9b469fe29b87 (patch)
treebdffd5d80c26cf4a7a518281a204be1ace85b4c1 /vendor/gix/src/head
parentReleasing progress-linux version 1.70.0+dfsg1-9~progress7.99u1. (diff)
downloadrustc-10ee2acdd26a7f1298c6f6d6b7af9b469fe29b87.tar.xz
rustc-10ee2acdd26a7f1298c6f6d6b7af9b469fe29b87.zip
Merging upstream version 1.70.0+dfsg2.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'vendor/gix/src/head')
-rw-r--r--vendor/gix/src/head/log.rs35
-rw-r--r--vendor/gix/src/head/mod.rs122
-rw-r--r--vendor/gix/src/head/peel.rs119
3 files changed, 276 insertions, 0 deletions
diff --git a/vendor/gix/src/head/log.rs b/vendor/gix/src/head/log.rs
new file mode 100644
index 000000000..6aa7ed1d3
--- /dev/null
+++ b/vendor/gix/src/head/log.rs
@@ -0,0 +1,35 @@
+use std::convert::TryInto;
+
+use gix_hash::ObjectId;
+
+use crate::{
+ bstr::{BString, ByteSlice},
+ Head,
+};
+
+impl<'repo> Head<'repo> {
+ /// Return a platform for obtaining iterators on the reference log associated with the `HEAD` reference.
+ pub fn log_iter(&self) -> gix_ref::file::log::iter::Platform<'static, 'repo> {
+ gix_ref::file::log::iter::Platform {
+ store: &self.repo.refs,
+ name: "HEAD".try_into().expect("HEAD is always valid"),
+ buf: Vec::new(),
+ }
+ }
+
+ /// Return a list of all branch names that were previously checked out with the first-ever checked out branch
+ /// being the first entry of the list, and the most recent is the last, along with the commit they were pointing to
+ /// at the time.
+ pub fn prior_checked_out_branches(&self) -> std::io::Result<Option<Vec<(BString, ObjectId)>>> {
+ Ok(self.log_iter().all()?.map(|log| {
+ log.filter_map(Result::ok)
+ .filter_map(|line| {
+ line.message
+ .strip_prefix(b"checkout: moving from ")
+ .and_then(|from_to| from_to.find(" to ").map(|pos| &from_to[..pos]))
+ .map(|from_branch| (from_branch.as_bstr().to_owned(), line.previous_oid()))
+ })
+ .collect()
+ }))
+ }
+}
diff --git a/vendor/gix/src/head/mod.rs b/vendor/gix/src/head/mod.rs
new file mode 100644
index 000000000..094e78a86
--- /dev/null
+++ b/vendor/gix/src/head/mod.rs
@@ -0,0 +1,122 @@
+//!
+use std::convert::TryInto;
+
+use gix_hash::ObjectId;
+use gix_ref::FullNameRef;
+
+use crate::{
+ ext::{ObjectIdExt, ReferenceExt},
+ Head,
+};
+
+/// Represents the kind of `HEAD` reference.
+#[derive(Clone)]
+pub enum Kind {
+ /// The existing reference the symbolic HEAD points to.
+ ///
+ /// This is the common case.
+ Symbolic(gix_ref::Reference),
+ /// The yet-to-be-created reference the symbolic HEAD refers to.
+ ///
+ /// This is the case in a newly initialized repository.
+ Unborn(gix_ref::FullName),
+ /// The head points to an object directly, not to a symbolic reference.
+ ///
+ /// This state is less common and can occur when checking out commits directly.
+ Detached {
+ /// The object to which the head points to
+ target: ObjectId,
+ /// Possibly the final destination of `target` after following the object chain from tag objects to commits.
+ peeled: Option<ObjectId>,
+ },
+}
+
+impl Kind {
+ /// Attach this instance to a `repo` to produce a [`Head`].
+ pub fn attach(self, repo: &crate::Repository) -> Head<'_> {
+ Head { kind: self, repo }
+ }
+}
+
+/// Access
+impl<'repo> Head<'repo> {
+ /// Returns the name of this references, always `HEAD`.
+ pub fn name(&self) -> &'static FullNameRef {
+ // TODO: use a statically checked version of this when available.
+ "HEAD".try_into().expect("HEAD is valid")
+ }
+
+ /// Returns the full reference name of this head if it is not detached, or `None` otherwise.
+ pub fn referent_name(&self) -> Option<&FullNameRef> {
+ Some(match &self.kind {
+ Kind::Symbolic(r) => r.name.as_ref(),
+ Kind::Unborn(name) => name.as_ref(),
+ Kind::Detached { .. } => return None,
+ })
+ }
+
+ /// Returns true if this instance is detached, and points to an object directly.
+ pub fn is_detached(&self) -> bool {
+ matches!(self.kind, Kind::Detached { .. })
+ }
+
+ /// Returns true if this instance is not yet born, hence it points to a ref that doesn't exist yet.
+ ///
+ /// This is the case in a newly initialized repository.
+ pub fn is_unborn(&self) -> bool {
+ matches!(self.kind, Kind::Unborn(_))
+ }
+
+ // TODO: tests
+ /// Returns the id the head points to, which isn't possible on unborn heads.
+ pub fn id(&self) -> Option<crate::Id<'repo>> {
+ match &self.kind {
+ Kind::Symbolic(r) => r.target.try_id().map(|oid| oid.to_owned().attach(self.repo)),
+ Kind::Detached { peeled, target } => {
+ (*peeled).unwrap_or_else(|| target.to_owned()).attach(self.repo).into()
+ }
+ Kind::Unborn(_) => None,
+ }
+ }
+
+ /// Try to transform this instance into the symbolic reference that it points to, or return `None` if head is detached or unborn.
+ pub fn try_into_referent(self) -> Option<crate::Reference<'repo>> {
+ match self.kind {
+ Kind::Symbolic(r) => r.attach(self.repo).into(),
+ _ => None,
+ }
+ }
+}
+
+mod remote {
+ use super::Head;
+ use crate::{remote, Remote};
+
+ /// Remote
+ impl<'repo> Head<'repo> {
+ /// Return the remote with which the currently checked our reference can be handled as configured by `branch.<name>.remote|pushRemote`
+ /// or fall back to the non-branch specific remote configuration. `None` is returned if the head is detached or unborn, so there is
+ /// no branch specific remote.
+ ///
+ /// This is equivalent to calling [`Reference::remote(…)`][crate::Reference::remote()] and
+ /// [`Repository::remote_default_name()`][crate::Repository::remote_default_name()] in order.
+ ///
+ /// Combine it with [`find_default_remote()`][crate::Repository::find_default_remote()] as fallback to handle detached heads,
+ /// i.e. obtain a remote even in case of detached heads.
+ pub fn into_remote(
+ self,
+ direction: remote::Direction,
+ ) -> Option<Result<Remote<'repo>, remote::find::existing::Error>> {
+ let repo = self.repo;
+ self.try_into_referent()?
+ .remote(direction)
+ .or_else(|| repo.find_default_remote(direction))
+ }
+ }
+}
+
+///
+pub mod log;
+
+///
+pub mod peel;
diff --git a/vendor/gix/src/head/peel.rs b/vendor/gix/src/head/peel.rs
new file mode 100644
index 000000000..65a876bc4
--- /dev/null
+++ b/vendor/gix/src/head/peel.rs
@@ -0,0 +1,119 @@
+use crate::{
+ ext::{ObjectIdExt, ReferenceExt},
+ Head,
+};
+
+mod error {
+ use crate::{object, reference};
+
+ /// The error returned by [Head::peel_to_id_in_place()][super::Head::peel_to_id_in_place()] and [Head::into_fully_peeled_id()][super::Head::into_fully_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;
+
+use crate::head::Kind;
+
+///
+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)]
+ 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> {
+ // TODO: tests
+ /// Peel this instance to make obtaining its final target id possible, while returning an error on unborn heads.
+ pub fn peeled(mut self) -> Result<Self, Error> {
+ self.peel_to_id_in_place().transpose()?;
+ Ok(self)
+ }
+
+ // TODO: tests
+ /// 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 `None` if the head is unborn.
+ pub fn peel_to_id_in_place(&mut self) -> Option<Result<crate::Id<'repo>, Error>> {
+ Some(match &mut self.kind {
+ Kind::Unborn(_name) => return None,
+ Kind::Detached {
+ peeled: Some(peeled), ..
+ } => Ok((*peeled).attach(self.repo)),
+ Kind::Detached { peeled: None, target } => {
+ match target
+ .attach(self.repo)
+ .object()
+ .map_err(Into::into)
+ .and_then(|obj| obj.peel_tags_to_end().map_err(Into::into))
+ .map(|peeled| peeled.id)
+ {
+ Ok(peeled) => {
+ self.kind = Kind::Detached {
+ peeled: Some(peeled),
+ target: *target,
+ };
+ Ok(peeled.attach(self.repo))
+ }
+ Err(err) => Err(err),
+ }
+ }
+ Kind::Symbolic(r) => {
+ let mut nr = r.clone().attach(self.repo);
+ let peeled = nr.peel_to_id_in_place().map_err(Into::into);
+ *r = nr.detach();
+ peeled
+ }
+ })
+ }
+
+ // TODO: tests
+ // TODO: something similar in `crate::Reference`
+ /// 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> {
+ let id = self.peel_to_id_in_place().ok_or_else(|| to_commit::Error::Unborn {
+ name: self.referent_name().expect("unborn").to_owned(),
+ })??;
+ id.object()
+ .map_err(|err| to_commit::Error::Peel(Error::FindExistingObject(err)))
+ .and_then(|object| object.try_into_commit().map_err(Into::into))
+ }
+
+ /// Consume this instance and transform it into the final object that it points to, or `None` if the `HEAD`
+ /// reference is yet to be born.
+ pub fn into_fully_peeled_id(self) -> Option<Result<crate::Id<'repo>, Error>> {
+ Some(match self.kind {
+ Kind::Unborn(_name) => return None,
+ Kind::Detached {
+ peeled: Some(peeled), ..
+ } => Ok(peeled.attach(self.repo)),
+ Kind::Detached { peeled: None, target } => target
+ .attach(self.repo)
+ .object()
+ .map_err(Into::into)
+ .and_then(|obj| obj.peel_tags_to_end().map_err(Into::into))
+ .map(|obj| obj.id.attach(self.repo)),
+ Kind::Symbolic(r) => r.attach(self.repo).peel_to_id_in_place().map_err(Into::into),
+ })
+ }
+}