summaryrefslogtreecommitdiffstats
path: root/vendor/gix/src/submodule
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-30 18:31:44 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-30 18:31:44 +0000
commitc23a457e72abe608715ac76f076f47dc42af07a5 (patch)
tree2772049aaf84b5c9d0ed12ec8d86812f7a7904b6 /vendor/gix/src/submodule
parentReleasing progress-linux version 1.73.0+dfsg1-1~progress7.99u1. (diff)
downloadrustc-c23a457e72abe608715ac76f076f47dc42af07a5.tar.xz
rustc-c23a457e72abe608715ac76f076f47dc42af07a5.zip
Merging upstream version 1.74.1+dfsg1.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'vendor/gix/src/submodule')
-rw-r--r--vendor/gix/src/submodule/errors.rs106
-rw-r--r--vendor/gix/src/submodule/mod.rs287
2 files changed, 393 insertions, 0 deletions
diff --git a/vendor/gix/src/submodule/errors.rs b/vendor/gix/src/submodule/errors.rs
new file mode 100644
index 000000000..4e41337de
--- /dev/null
+++ b/vendor/gix/src/submodule/errors.rs
@@ -0,0 +1,106 @@
+///
+pub mod open_modules_file {
+ /// The error returned by [Repository::open_modules_file()](crate::Repository::open_modules_file()).
+ #[derive(Debug, thiserror::Error)]
+ #[allow(missing_docs)]
+ pub enum Error {
+ #[error(transparent)]
+ Configuration(#[from] gix_config::parse::Error),
+ #[error("Could not read '.gitmodules' file")]
+ Io(#[from] std::io::Error),
+ }
+}
+
+///
+pub mod modules {
+ /// The error returned by [Repository::modules()](crate::Repository::modules()).
+ #[derive(Debug, thiserror::Error)]
+ #[allow(missing_docs)]
+ pub enum Error {
+ #[error(transparent)]
+ OpenModulesFile(#[from] crate::submodule::open_modules_file::Error),
+ #[error(transparent)]
+ OpenIndex(#[from] crate::worktree::open_index::Error),
+ #[error("Could not find the .gitmodules file by id in the object database")]
+ FindExistingBlob(#[from] crate::object::find::existing::Error),
+ #[error("Did not find commit in current HEAD to access its tree")]
+ FindHeadCommit(#[from] crate::reference::head_commit::Error),
+ #[error(transparent)]
+ TreeFromCommit(#[from] crate::object::commit::Error),
+ }
+}
+
+///
+pub mod is_active {
+ /// The error returned by [Submodule::is_active()](crate::Submodule::is_active()).
+ #[derive(Debug, thiserror::Error)]
+ #[allow(missing_docs)]
+ pub enum Error {
+ #[error(transparent)]
+ InitIsActivePlatform(#[from] gix_submodule::is_active_platform::Error),
+ #[error(transparent)]
+ QueryIsActive(#[from] gix_config::value::Error),
+ #[error(transparent)]
+ InitAttributes(#[from] crate::config::attribute_stack::Error),
+ #[error(transparent)]
+ InitPathspecDefaults(#[from] gix_pathspec::defaults::from_environment::Error),
+ #[error(transparent)]
+ ObtainIndex(#[from] crate::repository::index_or_load_from_head::Error),
+ }
+}
+
+///
+pub mod fetch_recurse {
+ /// The error returned by [Submodule::fetch_recurse()](crate::Submodule::fetch_recurse()).
+ #[derive(Debug, thiserror::Error)]
+ #[allow(missing_docs)]
+ pub enum Error {
+ #[error(transparent)]
+ ModuleBoolean(#[from] gix_submodule::config::Error),
+ #[error(transparent)]
+ ConfigurationFallback(#[from] crate::config::key::GenericErrorWithValue),
+ }
+}
+
+///
+pub mod open {
+ /// The error returned by [Submodule::open()](crate::Submodule::open()).
+ #[derive(Debug, thiserror::Error)]
+ #[allow(missing_docs)]
+ pub enum Error {
+ #[error(transparent)]
+ OpenRepository(#[from] crate::open::Error),
+ #[error(transparent)]
+ PathConfiguration(#[from] gix_submodule::config::path::Error),
+ }
+}
+
+///
+pub mod index_id {
+ /// The error returned by [Submodule::index_id()](crate::Submodule::index_id()).
+ #[derive(Debug, thiserror::Error)]
+ #[allow(missing_docs)]
+ pub enum Error {
+ #[error(transparent)]
+ PathConfiguration(#[from] gix_submodule::config::path::Error),
+ #[error(transparent)]
+ Index(#[from] crate::repository::index_or_load_from_head::Error),
+ }
+}
+
+///
+pub mod head_id {
+ /// The error returned by [Submodule::head_id()](crate::Submodule::head_id()).
+ #[derive(Debug, thiserror::Error)]
+ #[allow(missing_docs)]
+ pub enum Error {
+ #[error(transparent)]
+ HeadCommit(#[from] crate::reference::head_commit::Error),
+ #[error("Could not get tree of head commit")]
+ CommitTree(#[from] crate::object::commit::Error),
+ #[error("Could not peel tree to submodule path")]
+ PeelTree(#[from] crate::object::find::existing::Error),
+ #[error(transparent)]
+ PathConfiguration(#[from] gix_submodule::config::path::Error),
+ }
+}
diff --git a/vendor/gix/src/submodule/mod.rs b/vendor/gix/src/submodule/mod.rs
new file mode 100644
index 000000000..52c5938fc
--- /dev/null
+++ b/vendor/gix/src/submodule/mod.rs
@@ -0,0 +1,287 @@
+#![allow(clippy::result_large_err)]
+//! Submodule plumbing and abstractions
+//!
+use std::{
+ borrow::Cow,
+ cell::{Ref, RefCell, RefMut},
+ path::PathBuf,
+};
+
+use gix_odb::FindExt;
+pub use gix_submodule::*;
+
+use crate::{bstr::BStr, repository::IndexPersistedOrInMemory, Repository, Submodule};
+
+pub(crate) type ModulesFileStorage = gix_features::threading::OwnShared<gix_fs::SharedFileSnapshotMut<File>>;
+/// A lazily loaded and auto-updated worktree index.
+pub type ModulesSnapshot = gix_fs::SharedFileSnapshot<File>;
+
+/// The name of the file containing (sub) module information.
+pub(crate) const MODULES_FILE: &str = ".gitmodules";
+
+mod errors;
+pub use errors::*;
+
+/// A platform maintaining state needed to interact with submodules, created by [`Repository::submodules()].
+pub(crate) struct SharedState<'repo> {
+ pub(crate) repo: &'repo Repository,
+ pub(crate) modules: ModulesSnapshot,
+ is_active: RefCell<Option<IsActiveState>>,
+ index: RefCell<Option<IndexPersistedOrInMemory>>,
+}
+
+impl<'repo> SharedState<'repo> {
+ pub(crate) fn new(repo: &'repo Repository, modules: ModulesSnapshot) -> Self {
+ SharedState {
+ repo,
+ modules,
+ is_active: RefCell::new(None),
+ index: RefCell::new(None),
+ }
+ }
+
+ fn index(&self) -> Result<Ref<'_, IndexPersistedOrInMemory>, crate::repository::index_or_load_from_head::Error> {
+ {
+ let mut state = self.index.borrow_mut();
+ if state.is_none() {
+ *state = self.repo.index_or_load_from_head()?.into();
+ }
+ }
+ Ok(Ref::map(self.index.borrow(), |opt| {
+ opt.as_ref().expect("just initialized")
+ }))
+ }
+
+ fn active_state_mut(
+ &self,
+ ) -> Result<(RefMut<'_, IsActivePlatform>, RefMut<'_, gix_worktree::Stack>), is_active::Error> {
+ let mut state = self.is_active.borrow_mut();
+ if state.is_none() {
+ let platform = self
+ .modules
+ .is_active_platform(&self.repo.config.resolved, self.repo.config.pathspec_defaults()?)?;
+ let index = self.index()?;
+ let attributes = self
+ .repo
+ .attributes_only(
+ &index,
+ gix_worktree::stack::state::attributes::Source::WorktreeThenIdMapping
+ .adjust_for_bare(self.repo.is_bare()),
+ )?
+ .detach();
+ *state = Some(IsActiveState { platform, attributes });
+ }
+ Ok(RefMut::map_split(state, |opt| {
+ let state = opt.as_mut().expect("populated above");
+ (&mut state.platform, &mut state.attributes)
+ }))
+ }
+}
+
+struct IsActiveState {
+ platform: IsActivePlatform,
+ attributes: gix_worktree::Stack,
+}
+
+///Access
+impl<'repo> Submodule<'repo> {
+ /// Return the submodule's name.
+ pub fn name(&self) -> &BStr {
+ self.name.as_ref()
+ }
+ /// Return the path at which the submodule can be found, relative to the repository.
+ ///
+ /// For details, see [gix_submodule::File::path()].
+ pub fn path(&self) -> Result<Cow<'_, BStr>, config::path::Error> {
+ self.state.modules.path(self.name())
+ }
+
+ /// Return the url from which to clone or update the submodule.
+ pub fn url(&self) -> Result<gix_url::Url, config::url::Error> {
+ self.state.modules.url(self.name())
+ }
+
+ /// Return the `update` field from this submodule's configuration, if present, or `None`.
+ pub fn update(&self) -> Result<Option<config::Update>, config::update::Error> {
+ self.state.modules.update(self.name())
+ }
+
+ /// Return the `branch` field from this submodule's configuration, if present, or `None`.
+ pub fn branch(&self) -> Result<Option<config::Branch>, config::branch::Error> {
+ self.state.modules.branch(self.name())
+ }
+
+ /// Return the `fetchRecurseSubmodules` field from this submodule's configuration, or retrieve the value from `fetch.recurseSubmodules` if unset.
+ pub fn fetch_recurse(&self) -> Result<Option<config::FetchRecurse>, fetch_recurse::Error> {
+ Ok(match self.state.modules.fetch_recurse(self.name())? {
+ Some(val) => Some(val),
+ None => self
+ .state
+ .repo
+ .config
+ .resolved
+ .boolean_by_key("fetch.recurseSubmodules")
+ .map(|res| crate::config::tree::Fetch::RECURSE_SUBMODULES.try_into_recurse_submodules(res))
+ .transpose()?,
+ })
+ }
+
+ /// Return the `ignore` field from this submodule's configuration, if present, or `None`.
+ pub fn ignore(&self) -> Result<Option<config::Ignore>, config::Error> {
+ self.state.modules.ignore(self.name())
+ }
+
+ /// Return the `shallow` field from this submodule's configuration, if present, or `None`.
+ ///
+ /// If `true`, the submodule will be checked out with `depth = 1`. If unset, `false` is assumed.
+ pub fn shallow(&self) -> Result<Option<bool>, gix_config::value::Error> {
+ self.state.modules.shallow(self.name())
+ }
+
+ /// Returns true if this submodule is considered active and can thus participate in an operation.
+ ///
+ /// Please see the [plumbing crate documentation](gix_submodule::IsActivePlatform::is_active()) for details.
+ pub fn is_active(&self) -> Result<bool, is_active::Error> {
+ let (mut platform, mut attributes) = self.state.active_state_mut()?;
+ let is_active = platform.is_active(&self.state.repo.config.resolved, self.name.as_ref(), {
+ &mut |relative_path, case, is_dir, out| {
+ attributes
+ .set_case(case)
+ .at_entry(relative_path, Some(is_dir), |id, buf| {
+ self.state.repo.objects.find_blob(id, buf)
+ })
+ .map_or(false, |platform| platform.matching_attributes(out))
+ }
+ })?;
+ Ok(is_active)
+ }
+
+ /// Return the object id of the submodule as stored in the index of the superproject,
+ /// or `None` if it was deleted from the index.
+ ///
+ /// If `None`, but `Some()` when calling [`Self::head_id()`], then the submodule was just deleted but the change
+ /// wasn't yet committed. Note that `None` is also returned if the entry at the submodule path isn't a submodule.
+ /// If `Some()`, but `None` when calling [`Self::head_id()`], then the submodule was just added without having committed the change.
+ pub fn index_id(&self) -> Result<Option<gix_hash::ObjectId>, index_id::Error> {
+ let path = self.path()?;
+ Ok(self
+ .state
+ .index()?
+ .entry_by_path(&path)
+ .and_then(|entry| (entry.mode == gix_index::entry::Mode::COMMIT).then_some(entry.id)))
+ }
+
+ /// Return the object id of the submodule as stored in `HEAD^{tree}` of the superproject, or `None` if it wasn't yet committed.
+ ///
+ /// If `Some()`, but `None` when calling [`Self::index_id()`], then the submodule was just deleted but the change
+ /// wasn't yet committed. Note that `None` is also returned if the entry at the submodule path isn't a submodule.
+ /// If `None`, but `Some()` when calling [`Self::index_id()`], then the submodule was just added without having committed the change.
+ pub fn head_id(&self) -> Result<Option<gix_hash::ObjectId>, head_id::Error> {
+ let path = self.path()?;
+ Ok(self
+ .state
+ .repo
+ .head_commit()?
+ .tree()?
+ .peel_to_entry_by_path(gix_path::from_bstr(path.as_ref()))?
+ .and_then(|entry| (entry.mode() == gix_object::tree::EntryMode::Commit).then_some(entry.inner.oid)))
+ }
+
+ /// Return the path at which the repository of the submodule should be located.
+ ///
+ /// The directory might not exist yet.
+ pub fn git_dir(&self) -> PathBuf {
+ self.state
+ .repo
+ .common_dir()
+ .join("modules")
+ .join(gix_path::from_bstr(self.name()))
+ }
+
+ /// Return the path to the location at which the workdir would be checked out.
+ ///
+ /// Note that it may be a path relative to the repository if, for some reason, the parent directory
+ /// doesn't have a working dir set.
+ pub fn work_dir(&self) -> Result<PathBuf, config::path::Error> {
+ let worktree_git = gix_path::from_bstr(self.path()?);
+ Ok(match self.state.repo.work_dir() {
+ None => worktree_git.into_owned(),
+ Some(prefix) => prefix.join(worktree_git),
+ })
+ }
+
+ /// Return the path at which the repository of the submodule should be located, or the path inside of
+ /// the superproject's worktree where it actually *is* located if the submodule in the 'old-form', thus is a directory
+ /// inside of the superproject's work-tree.
+ ///
+ /// Note that 'old-form' paths returned aren't verified, i.e. the `.git` repository might be corrupt or otherwise
+ /// invalid - it's left to the caller to try to open it.
+ ///
+ /// Also note that the returned path may not actually exist.
+ pub fn git_dir_try_old_form(&self) -> Result<PathBuf, config::path::Error> {
+ let worktree_git = self.work_dir()?.join(gix_discover::DOT_GIT_DIR);
+ Ok(if worktree_git.is_dir() {
+ worktree_git
+ } else {
+ self.git_dir()
+ })
+ }
+
+ /// Query various parts of the submodule and assemble it into state information.
+ #[doc(alias = "status", alias = "git2")]
+ pub fn state(&self) -> Result<State, config::path::Error> {
+ let maybe_old_path = self.git_dir_try_old_form()?;
+ let git_dir = self.git_dir();
+ let worktree_git = self.work_dir()?.join(gix_discover::DOT_GIT_DIR);
+ let superproject_configuration = self
+ .state
+ .repo
+ .config
+ .resolved
+ .sections_by_name("submodule")
+ .into_iter()
+ .flatten()
+ .any(|section| section.header().subsection_name() == Some(self.name.as_ref()));
+ Ok(State {
+ repository_exists: maybe_old_path.is_dir(),
+ is_old_form: maybe_old_path != git_dir,
+ worktree_checkout: worktree_git.exists(),
+ superproject_configuration,
+ })
+ }
+
+ /// Open the submodule as repository, or `None` if the submodule wasn't initialized yet.
+ ///
+ /// More states can be derived here:
+ ///
+ /// * *initialized* - a repository exists, i.e. `Some(repo)` and the working tree is present.
+ /// * *uninitialized* - a repository does not exist, i.e. `None`
+ /// * *deinitialized* - a repository does exist, i.e. `Some(repo)`, but its working tree is empty.
+ ///
+ /// Also see the [state()](Self::state()) method for learning about the submodule.
+ /// The repository can also be used to learn about the submodule `HEAD`, i.e. where its working tree is at,
+ /// which may differ compared to the superproject's index or `HEAD` commit.
+ pub fn open(&self) -> Result<Option<Repository>, open::Error> {
+ match crate::open_opts(self.git_dir_try_old_form()?, self.state.repo.options.clone()) {
+ Ok(repo) => Ok(Some(repo)),
+ Err(crate::open::Error::NotARepository { .. }) => Ok(None),
+ Err(err) => Err(err.into()),
+ }
+ }
+}
+
+/// A summary of the state of all parts forming a submodule, which allows to answer various questions about it.
+///
+/// Note that expensive questions about its presence in the `HEAD` or the `index` are left to the caller.
+#[derive(Default, Copy, Clone, Debug, Ord, PartialOrd, Eq, PartialEq, Hash)]
+pub struct State {
+ /// if the submodule repository has been cloned.
+ pub repository_exists: bool,
+ /// if the submodule repository is located directly in the worktree of the superproject.
+ pub is_old_form: bool,
+ /// if the worktree is checked out.
+ pub worktree_checkout: bool,
+ /// If submodule configuration was found in the superproject's `.git/config` file.
+ /// Note that the presence of a single section is enough, independently of the actual values.
+ pub superproject_configuration: bool,
+}