| use bstr::{BStr, BString, ByteSlice}; |
| |
| /// Determine how the submodule participates in `git status` queries. This setting also affects `git diff`. |
| #[derive(Default, Debug, Clone, Copy, Ord, PartialOrd, Eq, PartialEq, Hash)] |
| pub enum Ignore { |
| /// Submodule changes won't be considered at all, which is the fastest option. |
| All, |
| /// Ignore any changes to the submodule working tree, only show committed differences between the `HEAD` of the submodule |
| /// compared to the recorded commit in the superproject. |
| Dirty, |
| /// Only ignore untracked files in the submodule, but show modifications to the submodule working tree as well as differences |
| /// between the recorded commit in the superproject and the checked-out commit in the submodule. |
| Untracked, |
| /// No modifications to the submodule are ignored, which shows untracked files, modified files in the submodule worktree as well as |
| /// differences between the recorded commit in the superproject and the checked-out commit in the submodule. |
| #[default] |
| None, |
| } |
| |
| impl TryFrom<&BStr> for Ignore { |
| type Error = (); |
| |
| fn try_from(value: &BStr) -> Result<Self, Self::Error> { |
| Ok(match value.as_bytes() { |
| b"all" => Ignore::All, |
| b"dirty" => Ignore::Dirty, |
| b"untracked" => Ignore::Untracked, |
| b"none" => Ignore::None, |
| _ => return Err(()), |
| }) |
| } |
| } |
| |
| /// Determine how to recurse into this module from the superproject when fetching. |
| /// |
| /// Generally, a fetch is only performed if the submodule commit referenced by the superproject isn't already |
| /// present in the submodule repository. |
| /// |
| /// Note that when unspecified, the `fetch.recurseSubmodules` configuration variable should be used instead. |
| #[derive(Default, Debug, Clone, Copy, Ord, PartialOrd, Eq, PartialEq, Hash)] |
| pub enum FetchRecurse { |
| /// Fetch only changed submodules. |
| #[default] |
| OnDemand, |
| /// Fetch all populated submodules, changed or not. |
| /// |
| /// This skips the work needed to determine whether a submodule has changed in the first place, but may work |
| /// more as some fetches might not be necessary. |
| Always, |
| /// Submodules are never fetched. |
| Never, |
| } |
| |
| impl FetchRecurse { |
| /// Check if `boolean` is set and translate it the respective variant, or check the underlying string |
| /// value for non-boolean options. |
| /// On error, it returns the obtained string value which would be the invalid value. |
| pub fn new(boolean: Result<bool, gix_config::value::Error>) -> Result<Self, BString> { |
| Ok(match boolean { |
| Ok(value) => { |
| if value { |
| FetchRecurse::Always |
| } else { |
| FetchRecurse::Never |
| } |
| } |
| Err(err) => { |
| if err.input != "on-demand" { |
| return Err(err.input); |
| } |
| FetchRecurse::OnDemand |
| } |
| }) |
| } |
| } |
| |
| /// Describes the branch that should be tracked on the remote. |
| #[derive(Debug, Clone, Ord, PartialOrd, Eq, PartialEq, Hash)] |
| pub enum Branch { |
| /// The name of the remote branch should be the same as the one currently checked out in the superproject. |
| CurrentInSuperproject, |
| /// The validated remote-only branch that could be used for fetching. |
| Name(BString), |
| } |
| |
| impl Default for Branch { |
| fn default() -> Self { |
| Branch::Name("HEAD".into()) |
| } |
| } |
| |
| impl TryFrom<&BStr> for Branch { |
| type Error = gix_refspec::parse::Error; |
| |
| fn try_from(value: &BStr) -> Result<Self, Self::Error> { |
| if value == "." { |
| return Ok(Branch::CurrentInSuperproject); |
| } |
| |
| gix_refspec::parse(value, gix_refspec::parse::Operation::Fetch) |
| .map(|spec| Branch::Name(spec.source().expect("no object").to_owned())) |
| } |
| } |
| |
| /// Determine how `git submodule update` should deal with this submodule to bring it up-to-date with the |
| /// super-project's expectations. |
| #[derive(Default, Debug, Clone, Hash, PartialOrd, PartialEq, Ord, Eq)] |
| pub enum Update { |
| /// The commit recorded in the superproject should be checked out on a detached `HEAD`. |
| #[default] |
| Checkout, |
| /// The current branch in the submodule will be rebased onto the commit recorded in the superproject. |
| Rebase, |
| /// The commit recorded in the superproject will merged into the current branch of the submodule. |
| Merge, |
| /// A custom command to be called like `<command> hash-of-submodule-commit` that is to be executed to |
| /// perform the submodule update. |
| /// |
| /// Note that this variant is only allowed if the value is coming from an override. Thus it's not allowed to distribute |
| /// arbitrary commands via `.gitmodules` for security reasons. |
| Command(BString), |
| /// The submodule update is not performed at all. |
| None, |
| } |
| |
| impl TryFrom<&BStr> for Update { |
| type Error = (); |
| |
| fn try_from(value: &BStr) -> Result<Self, Self::Error> { |
| Ok(match value.as_bstr().as_bytes() { |
| b"checkout" => Update::Checkout, |
| b"rebase" => Update::Rebase, |
| b"merge" => Update::Merge, |
| b"none" => Update::None, |
| command if command.first() == Some(&b'!') => Update::Command(command[1..].to_owned().into()), |
| _ => return Err(()), |
| }) |
| } |
| } |
| |
| /// The error returned by [File::fetch_recurse()](crate::File::fetch_recurse) and [File::ignore()](crate::File::ignore). |
| #[derive(Debug, thiserror::Error)] |
| #[allow(missing_docs)] |
| #[error("The '{field}' field of submodule '{submodule}' was invalid: '{actual}'")] |
| pub struct Error { |
| pub field: &'static str, |
| pub submodule: BString, |
| pub actual: BString, |
| } |
| |
| /// |
| #[allow(clippy::empty_docs)] |
| pub mod branch { |
| use bstr::BString; |
| |
| /// The error returned by [File::branch()](crate::File::branch). |
| #[derive(Debug, thiserror::Error)] |
| #[allow(missing_docs)] |
| #[error("The value '{actual}' of the 'branch' field of submodule '{submodule}' couldn't be turned into a valid fetch refspec")] |
| pub struct Error { |
| pub submodule: BString, |
| pub actual: BString, |
| pub source: gix_refspec::parse::Error, |
| } |
| } |
| |
| /// |
| #[allow(clippy::empty_docs)] |
| pub mod update { |
| use bstr::BString; |
| |
| /// The error returned by [File::update()](crate::File::update). |
| #[derive(Debug, thiserror::Error)] |
| #[allow(missing_docs)] |
| pub enum Error { |
| #[error("The 'update' field of submodule '{submodule}' tried to set command '{actual}' to be shared")] |
| CommandForbiddenInModulesConfiguration { submodule: BString, actual: BString }, |
| #[error("The 'update' field of submodule '{submodule}' was invalid: '{actual}'")] |
| Invalid { submodule: BString, actual: BString }, |
| } |
| } |
| |
| /// |
| #[allow(clippy::empty_docs)] |
| pub mod url { |
| use bstr::BString; |
| |
| /// The error returned by [File::url()](crate::File::url). |
| #[derive(Debug, thiserror::Error)] |
| #[allow(missing_docs)] |
| pub enum Error { |
| #[error("The url of submodule '{submodule}' could not be parsed")] |
| Parse { |
| submodule: BString, |
| source: gix_url::parse::Error, |
| }, |
| #[error("The submodule '{submodule}' was missing its 'url' field or it was empty")] |
| Missing { submodule: BString }, |
| } |
| } |
| |
| /// |
| #[allow(clippy::empty_docs)] |
| pub mod path { |
| use bstr::BString; |
| |
| /// The error returned by [File::path()](crate::File::path). |
| #[derive(Debug, thiserror::Error)] |
| #[allow(missing_docs)] |
| pub enum Error { |
| #[error("The path '{actual}' of submodule '{submodule}' needs to be relative")] |
| Absolute { actual: BString, submodule: BString }, |
| #[error("The submodule '{submodule}' was missing its 'path' field or it was empty")] |
| Missing { submodule: BString }, |
| #[error("The path '{actual}' would lead outside of the repository worktree")] |
| OutsideOfWorktree { actual: BString, submodule: BString }, |
| } |
| } |