diff options
Diffstat (limited to 'vendor/gix/src/reference')
-rw-r--r-- | vendor/gix/src/reference/edits.rs | 75 | ||||
-rw-r--r-- | vendor/gix/src/reference/errors.rs | 89 | ||||
-rw-r--r-- | vendor/gix/src/reference/iter.rs | 127 | ||||
-rw-r--r-- | vendor/gix/src/reference/log.rs | 36 | ||||
-rw-r--r-- | vendor/gix/src/reference/mod.rs | 87 | ||||
-rw-r--r-- | vendor/gix/src/reference/remote.rs | 49 |
6 files changed, 463 insertions, 0 deletions
diff --git a/vendor/gix/src/reference/edits.rs b/vendor/gix/src/reference/edits.rs new file mode 100644 index 000000000..aadd087ba --- /dev/null +++ b/vendor/gix/src/reference/edits.rs @@ -0,0 +1,75 @@ +/// +pub mod set_target_id { + use gix_ref::{transaction::PreviousValue, Target}; + + use crate::{bstr::BString, Reference}; + + mod error { + use gix_ref::FullName; + + /// The error returned by [`Reference::set_target_id()`][super::Reference::set_target_id()]. + #[derive(Debug, thiserror::Error)] + #[allow(missing_docs)] + pub enum Error { + #[error("Cannot change symbolic reference {name:?} into a direct one by setting it to an id")] + SymbolicReference { name: FullName }, + #[error(transparent)] + ReferenceEdit(#[from] crate::reference::edit::Error), + } + } + pub use error::Error; + + impl<'repo> Reference<'repo> { + /// Set the id of this direct reference to `id` and use `reflog_message` for the reflog (if enabled in the repository). + /// + /// Note that the operation will fail on symbolic references, to change their type use the lower level reference database, + /// or if the reference was deleted or changed in the mean time. + /// Furthermore, refrain from using this method for more than a one-off change as it creates a transaction for each invocation. + /// If multiple reference should be changed, use [Repository::edit_references()][crate::Repository::edit_references()] + /// or the lower level reference database instead. + #[allow(clippy::result_large_err)] + pub fn set_target_id( + &mut self, + id: impl Into<gix_hash::ObjectId>, + reflog_message: impl Into<BString>, + ) -> Result<(), Error> { + match &self.inner.target { + Target::Symbolic(name) => return Err(Error::SymbolicReference { name: name.clone() }), + Target::Peeled(current_id) => { + let changed = self.repo.reference( + self.name(), + id, + PreviousValue::MustExistAndMatch(Target::Peeled(current_id.to_owned())), + reflog_message, + )?; + *self = changed; + } + } + Ok(()) + } + } +} + +/// +pub mod delete { + use gix_ref::transaction::{Change, PreviousValue, RefEdit, RefLog}; + + use crate::Reference; + + impl<'repo> Reference<'repo> { + /// Delete this reference or fail if it was changed since last observed. + /// Note that this instance remains available in memory but probably shouldn't be used anymore. + pub fn delete(&self) -> Result<(), crate::reference::edit::Error> { + self.repo + .edit_reference(RefEdit { + change: Change::Delete { + expected: PreviousValue::MustExistAndMatch(self.inner.target.clone()), + log: RefLog::AndReference, + }, + name: self.inner.name.clone(), + deref: false, + }) + .map(|_| ()) + } + } +} diff --git a/vendor/gix/src/reference/errors.rs b/vendor/gix/src/reference/errors.rs new file mode 100644 index 000000000..364456fd1 --- /dev/null +++ b/vendor/gix/src/reference/errors.rs @@ -0,0 +1,89 @@ +/// +pub mod edit { + use crate::config; + + /// The error returned by [edit_references(…)][crate::Repository::edit_references()], and others + /// which ultimately create a reference. + #[derive(Debug, thiserror::Error)] + #[allow(missing_docs)] + pub enum Error { + #[error(transparent)] + FileTransactionPrepare(#[from] gix_ref::file::transaction::prepare::Error), + #[error(transparent)] + FileTransactionCommit(#[from] gix_ref::file::transaction::commit::Error), + #[error(transparent)] + NameValidation(#[from] gix_validate::reference::name::Error), + #[error("Could not interpret core.filesRefLockTimeout or core.packedRefsTimeout, it must be the number in milliseconds to wait for locks or negative to wait forever")] + LockTimeoutConfiguration(#[from] config::lock_timeout::Error), + #[error(transparent)] + ParseCommitterTime(#[from] crate::config::time::Error), + } +} + +/// +pub mod peel { + /// The error returned by [Reference::peel_to_id_in_place(…)][crate::Reference::peel_to_id_in_place()] and + /// [Reference::into_fully_peeled_id(…)][crate::Reference::into_fully_peeled_id()]. + #[derive(Debug, thiserror::Error)] + #[allow(missing_docs)] + pub enum Error { + #[error(transparent)] + ToId(#[from] gix_ref::peel::to_id::Error), + #[error(transparent)] + PackedRefsOpen(#[from] gix_ref::packed::buffer::open::Error), + } +} + +/// +pub mod head_id { + /// The error returned by [Repository::head_id(…)][crate::Repository::head_id()]. + #[derive(Debug, thiserror::Error)] + #[allow(missing_docs)] + pub enum Error { + #[error(transparent)] + Head(#[from] crate::reference::find::existing::Error), + #[error(transparent)] + PeelToId(#[from] crate::head::peel::Error), + #[error("Branch '{name}' does not have any commits")] + Unborn { name: gix_ref::FullName }, + } +} + +/// +pub mod head_commit { + /// The error returned by [Repository::head_commit(…)][crate::Repository::head_commit()]. + #[derive(Debug, thiserror::Error)] + #[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), + } +} + +/// +pub mod find { + /// + pub mod existing { + /// The error returned by [find_reference(…)][crate::Repository::find_reference()], and others. + #[derive(Debug, thiserror::Error)] + #[allow(missing_docs)] + pub enum Error { + #[error(transparent)] + Find(#[from] crate::reference::find::Error), + #[error("The reference did not exist")] + NotFound, + } + } + + /// The error returned by [try_find_reference(…)][crate::Repository::try_find_reference()]. + #[derive(Debug, thiserror::Error)] + #[allow(missing_docs)] + pub enum Error { + #[error(transparent)] + Find(#[from] gix_ref::file::find::Error), + #[error(transparent)] + PackedRefsOpen(#[from] gix_ref::packed::buffer::open::Error), + } +} diff --git a/vendor/gix/src/reference/iter.rs b/vendor/gix/src/reference/iter.rs new file mode 100644 index 000000000..a2b022f64 --- /dev/null +++ b/vendor/gix/src/reference/iter.rs @@ -0,0 +1,127 @@ +//! +use std::path::Path; + +use gix_odb::pack::Find; +use gix_ref::file::ReferenceExt; + +/// A platform to create iterators over references. +#[must_use = "Iterators should be obtained from this iterator platform"] +pub struct Platform<'r> { + pub(crate) platform: gix_ref::file::iter::Platform<'r>, + pub(crate) repo: &'r crate::Repository, +} + +/// An iterator over references, with or without filter. +pub struct Iter<'r> { + inner: gix_ref::file::iter::LooseThenPacked<'r, 'r>, + peel: bool, + repo: &'r crate::Repository, +} + +impl<'r> Iter<'r> { + fn new(repo: &'r crate::Repository, platform: gix_ref::file::iter::LooseThenPacked<'r, 'r>) -> Self { + Iter { + inner: platform, + peel: false, + repo, + } + } +} + +impl<'r> Platform<'r> { + /// Return an iterator over all references in the repository. + /// + /// Even broken or otherwise unparsable or inaccessible references are returned and have to be handled by the caller on a + /// case by case basis. + pub fn all(&self) -> Result<Iter<'_>, init::Error> { + Ok(Iter::new(self.repo, self.platform.all()?)) + } + + /// Return an iterator over all references that match the given `prefix`. + /// + /// These are of the form `refs/heads` or `refs/remotes/origin`, and must not contain relative paths components like `.` or `..`. + // TODO: Create a custom `Path` type that enforces the requirements of git naturally, this type is surprising possibly on windows + // and when not using a trailing '/' to signal directories. + pub fn prefixed(&self, prefix: impl AsRef<Path>) -> Result<Iter<'_>, init::Error> { + Ok(Iter::new(self.repo, self.platform.prefixed(prefix)?)) + } + + // TODO: tests + /// Return an iterator over all references that are tags. + /// + /// They are all prefixed with `refs/tags`. + pub fn tags(&self) -> Result<Iter<'_>, init::Error> { + Ok(Iter::new(self.repo, self.platform.prefixed("refs/tags/")?)) + } + + // TODO: tests + /// Return an iterator over all local branches. + /// + /// They are all prefixed with `refs/heads`. + pub fn local_branches(&self) -> Result<Iter<'_>, init::Error> { + Ok(Iter::new(self.repo, self.platform.prefixed("refs/heads/")?)) + } + + // TODO: tests + /// Return an iterator over all remote branches. + /// + /// They are all prefixed with `refs/remotes`. + pub fn remote_branches(&self) -> Result<Iter<'_>, init::Error> { + Ok(Iter::new(self.repo, self.platform.prefixed("refs/remotes/")?)) + } +} + +impl<'r> Iter<'r> { + /// Automatically peel references before yielding them during iteration. + /// + /// This has the same effect as using `iter.map(|r| {r.peel_to_id_in_place(); r})`. + /// + /// # Note + /// + /// Doing this is necessary as the packed-refs buffer is already held by the iterator, disallowing the consumer of the iterator + /// to peel the returned references themselves. + pub fn peeled(mut self) -> Self { + self.peel = true; + self + } +} + +impl<'r> Iterator for Iter<'r> { + type Item = Result<crate::Reference<'r>, Box<dyn std::error::Error + Send + Sync + 'static>>; + + fn next(&mut self) -> Option<Self::Item> { + self.inner.next().map(|res| { + res.map_err(|err| Box::new(err) as Box<dyn std::error::Error + Send + Sync + 'static>) + .and_then(|mut r| { + if self.peel { + let handle = &self.repo; + r.peel_to_id_in_place(&handle.refs, |oid, buf| { + handle + .objects + .try_find(oid, buf) + .map(|po| po.map(|(o, _l)| (o.kind, o.data))) + }) + .map_err(|err| Box::new(err) as Box<dyn std::error::Error + Send + Sync + 'static>) + .map(|_| r) + } else { + Ok(r) + } + }) + .map(|r| crate::Reference::from_ref(r, self.repo)) + }) + } +} + +/// +pub mod init { + /// The error returned by [`Platform::all()`][super::Platform::all()] or [`Platform::prefixed()`][super::Platform::prefixed()]. + #[derive(Debug, thiserror::Error)] + #[allow(missing_docs)] + pub enum Error { + #[error(transparent)] + Io(#[from] std::io::Error), + } +} + +/// The error returned by [references()][crate::Repository::references()]. +pub type Error = gix_ref::packed::buffer::open::Error; diff --git a/vendor/gix/src/reference/log.rs b/vendor/gix/src/reference/log.rs new file mode 100644 index 000000000..b516e6499 --- /dev/null +++ b/vendor/gix/src/reference/log.rs @@ -0,0 +1,36 @@ +//! +use gix_object::commit::MessageRef; +use gix_ref::file::ReferenceExt; + +use crate::{ + bstr::{BStr, BString, ByteVec}, + Reference, +}; + +impl<'repo> Reference<'repo> { + /// Return a platform for obtaining iterators over reference logs. + pub fn log_iter(&self) -> gix_ref::file::log::iter::Platform<'_, '_> { + self.inner.log_iter(&self.repo.refs) + } +} + +/// Generate a message typical for git commit logs based on the given `operation`, commit `message` and `num_parents` of the commit. +pub fn message(operation: &str, message: &BStr, num_parents: usize) -> BString { + let mut out = BString::from(operation); + if let Some(commit_type) = commit_type_by_parents(num_parents) { + out.push_str(b" ("); + out.extend_from_slice(commit_type.as_bytes()); + out.push_byte(b')'); + } + out.push_str(b": "); + out.extend_from_slice(&MessageRef::from_bytes(message).summary()); + out +} + +pub(crate) fn commit_type_by_parents(count: usize) -> Option<&'static str> { + Some(match count { + 0 => "initial", + 1 => return None, + _two_or_more => "merge", + }) +} diff --git a/vendor/gix/src/reference/mod.rs b/vendor/gix/src/reference/mod.rs new file mode 100644 index 000000000..e2ee0d3b2 --- /dev/null +++ b/vendor/gix/src/reference/mod.rs @@ -0,0 +1,87 @@ +//! + +use gix_odb::pack::Find; +use gix_ref::file::ReferenceExt; + +use crate::{Id, Reference}; + +pub mod iter; +/// +pub mod remote; + +mod errors; +pub use errors::{edit, find, head_commit, head_id, peel}; + +use crate::ext::ObjectIdExt; + +pub mod log; + +pub use gix_ref::{Category, Kind}; + +/// Access +impl<'repo> Reference<'repo> { + /// Returns the attached id we point to, or `None` if this is a symbolic ref. + pub fn try_id(&self) -> Option<Id<'repo>> { + match self.inner.target { + gix_ref::Target::Symbolic(_) => None, + gix_ref::Target::Peeled(oid) => oid.to_owned().attach(self.repo).into(), + } + } + + /// Returns the attached id we point to, or panic if this is a symbolic ref. + pub fn id(&self) -> Id<'repo> { + self.try_id() + .expect("BUG: tries to obtain object id from symbolic target") + } + + /// Return the target to which this reference points to. + pub fn target(&self) -> gix_ref::TargetRef<'_> { + self.inner.target.to_ref() + } + + /// Return the reference's full name. + pub fn name(&self) -> &gix_ref::FullNameRef { + self.inner.name.as_ref() + } + + /// Turn this instances into a stand-alone reference. + pub fn detach(self) -> gix_ref::Reference { + self.inner + } +} + +impl<'repo> std::fmt::Debug for Reference<'repo> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Debug::fmt(&self.inner, f) + } +} + +impl<'repo> Reference<'repo> { + pub(crate) fn from_ref(reference: gix_ref::Reference, repo: &'repo crate::Repository) -> Self { + Reference { inner: reference, repo } + } +} + +impl<'repo> Reference<'repo> { + /// Follow all symbolic targets this reference might point to and peel the underlying object + /// to the end of the chain, and return it. + /// + /// This is useful to learn where this reference is ultimately pointing to. + pub fn peel_to_id_in_place(&mut self) -> Result<Id<'repo>, peel::Error> { + let repo = &self.repo; + let oid = self.inner.peel_to_id_in_place(&repo.refs, |oid, buf| { + repo.objects + .try_find(oid, buf) + .map(|po| po.map(|(o, _l)| (o.kind, o.data))) + })?; + Ok(Id::from_id(oid, repo)) + } + + /// Similar to [`peel_to_id_in_place()`][Reference::peel_to_id_in_place()], but consumes this instance. + pub fn into_fully_peeled_id(mut self) -> Result<Id<'repo>, peel::Error> { + self.peel_to_id_in_place() + } +} + +mod edits; +pub use edits::{delete, set_target_id}; diff --git a/vendor/gix/src/reference/remote.rs b/vendor/gix/src/reference/remote.rs new file mode 100644 index 000000000..dd96892e2 --- /dev/null +++ b/vendor/gix/src/reference/remote.rs @@ -0,0 +1,49 @@ +use crate::{config, config::tree::Branch, remote, Reference}; + +/// Remotes +impl<'repo> Reference<'repo> { + /// Find the unvalidated name of our remote for `direction` as configured in `branch.<name>.remote|pushRemote` respectively. + /// If `Some(<name>)` it can be used in [`Repository::find_remote(…)`][crate::Repository::find_remote()], or if `None` then + /// [Repository::remote_default_name()][crate::Repository::remote_default_name()] could be used in its place. + /// + /// Return `None` if no remote is configured. + /// + /// # Note + /// + /// - it's recommended to use the [`remote(…)`][Self::remote()] method as it will configure the remote with additional + /// information. + /// - `branch.<name>.pushRemote` falls back to `branch.<name>.remote`. + pub fn remote_name(&self, direction: remote::Direction) -> Option<remote::Name<'repo>> { + let name = self.name().shorten(); + let config = &self.repo.config.resolved; + (direction == remote::Direction::Push) + .then(|| { + config + .string("branch", Some(name), Branch::PUSH_REMOTE.name) + .or_else(|| config.string("remote", None, config::tree::Remote::PUSH_DEFAULT.name)) + }) + .flatten() + .or_else(|| config.string("branch", Some(name), Branch::REMOTE.name)) + .and_then(|name| name.try_into().ok()) + } + + /// Like [`remote_name(…)`][Self::remote_name()], but configures the returned `Remote` with additional information like + /// + /// - `branch.<name>.merge` to know which branch on the remote side corresponds to this one for merging when pulling. + /// + /// It also handles if the remote is a configured URL, which has no name. + pub fn remote( + &self, + direction: remote::Direction, + ) -> Option<Result<crate::Remote<'repo>, remote::find::existing::Error>> { + // TODO: use `branch.<name>.merge` + self.remote_name(direction).map(|name| match name { + remote::Name::Symbol(name) => self.repo.find_remote(name.as_ref()).map_err(Into::into), + remote::Name::Url(url) => gix_url::parse(url.as_ref()).map_err(Into::into).and_then(|url| { + self.repo + .remote_at(url) + .map_err(|err| remote::find::existing::Error::Find(remote::find::Error::Init(err))) + }), + }) + } +} |