summaryrefslogtreecommitdiffstats
path: root/vendor/gix/src
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-18 02:49:50 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-18 02:49:50 +0000
commit9835e2ae736235810b4ea1c162ca5e65c547e770 (patch)
tree3fcebf40ed70e581d776a8a4c65923e8ec20e026 /vendor/gix/src
parentReleasing progress-linux version 1.70.0+dfsg2-1~progress7.99u1. (diff)
downloadrustc-9835e2ae736235810b4ea1c162ca5e65c547e770.tar.xz
rustc-9835e2ae736235810b4ea1c162ca5e65c547e770.zip
Merging upstream version 1.71.1+dfsg1.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'vendor/gix/src')
-rw-r--r--vendor/gix/src/attributes.rs9
-rw-r--r--vendor/gix/src/clone/access.rs61
-rw-r--r--vendor/gix/src/clone/checkout.rs9
-rw-r--r--vendor/gix/src/clone/fetch/mod.rs85
-rw-r--r--vendor/gix/src/clone/mod.rs53
-rw-r--r--vendor/gix/src/commit.rs9
-rw-r--r--vendor/gix/src/config/cache/access.rs134
-rw-r--r--vendor/gix/src/config/cache/incubate.rs34
-rw-r--r--vendor/gix/src/config/cache/init.rs71
-rw-r--r--vendor/gix/src/config/cache/util.rs3
-rw-r--r--vendor/gix/src/config/mod.rs45
-rw-r--r--vendor/gix/src/config/tree/mod.rs9
-rw-r--r--vendor/gix/src/config/tree/sections/clone.rs4
-rw-r--r--vendor/gix/src/config/tree/sections/gitoxide.rs48
-rw-r--r--vendor/gix/src/config/tree/sections/index.rs62
-rw-r--r--vendor/gix/src/config/tree/sections/mod.rs5
-rw-r--r--vendor/gix/src/create.rs4
-rw-r--r--vendor/gix/src/diff.rs17
-rw-r--r--vendor/gix/src/id.rs12
-rw-r--r--vendor/gix/src/lib.rs95
-rw-r--r--vendor/gix/src/object/commit.rs4
-rw-r--r--vendor/gix/src/object/mod.rs10
-rw-r--r--vendor/gix/src/object/tag.rs11
-rw-r--r--vendor/gix/src/object/tree/diff/mod.rs9
-rw-r--r--vendor/gix/src/object/tree/mod.rs17
-rw-r--r--vendor/gix/src/open/mod.rs19
-rw-r--r--vendor/gix/src/open/options.rs5
-rw-r--r--vendor/gix/src/open/permissions.rs215
-rw-r--r--vendor/gix/src/open/repository.rs24
-rw-r--r--vendor/gix/src/prelude.rs4
-rw-r--r--vendor/gix/src/progress.rs3
-rw-r--r--vendor/gix/src/remote/connect.rs16
-rw-r--r--vendor/gix/src/remote/connection/access.rs22
-rw-r--r--vendor/gix/src/remote/connection/fetch/error.rs15
-rw-r--r--vendor/gix/src/remote/connection/fetch/mod.rs28
-rw-r--r--vendor/gix/src/remote/connection/fetch/negotiate.rs15
-rw-r--r--vendor/gix/src/remote/connection/fetch/receive_pack.rs130
-rw-r--r--vendor/gix/src/remote/connection/mod.rs3
-rw-r--r--vendor/gix/src/remote/connection/ref_map.rs21
-rw-r--r--vendor/gix/src/remote/fetch.rs50
-rw-r--r--vendor/gix/src/remote/save.rs2
-rw-r--r--vendor/gix/src/repository/attributes.rs50
-rw-r--r--vendor/gix/src/repository/excludes.rs45
-rw-r--r--vendor/gix/src/repository/impls.rs4
-rw-r--r--vendor/gix/src/repository/init.rs3
-rw-r--r--vendor/gix/src/repository/mod.rs4
-rw-r--r--vendor/gix/src/repository/object.rs54
-rw-r--r--vendor/gix/src/repository/permissions.rs119
-rw-r--r--vendor/gix/src/repository/shallow.rs65
-rw-r--r--vendor/gix/src/repository/worktree.rs24
-rw-r--r--vendor/gix/src/revision/spec/parse/types.rs11
-rw-r--r--vendor/gix/src/revision/walk.rs102
-rw-r--r--vendor/gix/src/shallow.rs92
-rw-r--r--vendor/gix/src/state.rs25
-rw-r--r--vendor/gix/src/types.rs2
-rw-r--r--vendor/gix/src/worktree/mod.rs105
56 files changed, 1600 insertions, 497 deletions
diff --git a/vendor/gix/src/attributes.rs b/vendor/gix/src/attributes.rs
new file mode 100644
index 000000000..bb8636460
--- /dev/null
+++ b/vendor/gix/src/attributes.rs
@@ -0,0 +1,9 @@
+/// The error returned by [`Repository::attributes()`][crate::Repository::attributes()].
+#[derive(Debug, thiserror::Error)]
+#[allow(missing_docs)]
+pub enum Error {
+ #[error(transparent)]
+ ConfigureAttributes(#[from] crate::config::attribute_stack::Error),
+ #[error(transparent)]
+ ConfigureExcludes(#[from] crate::config::exclude_stack::Error),
+}
diff --git a/vendor/gix/src/clone/access.rs b/vendor/gix/src/clone/access.rs
new file mode 100644
index 000000000..5b6e5fbab
--- /dev/null
+++ b/vendor/gix/src/clone/access.rs
@@ -0,0 +1,61 @@
+use crate::bstr::BString;
+use crate::clone::PrepareFetch;
+use crate::Repository;
+
+/// Builder
+impl PrepareFetch {
+ /// Use `f` to apply arbitrary changes to the remote that is about to be used to fetch a pack.
+ ///
+ /// The passed in `remote` will be un-named and pre-configured to be a default remote as we know it from git-clone.
+ /// It is not yet present in the configuration of the repository,
+ /// but each change it will eventually be written to the configuration prior to performing a the fetch operation,
+ /// _all changes done in `f()` will be persisted_.
+ ///
+ /// It can also be used to configure additional options, like those for fetching tags. Note that
+ /// [with_fetch_tags()][crate::Remote::with_fetch_tags()] should be called here to configure the clone as desired.
+ /// Otherwise a clone is configured to be complete and fetches all tags, not only those reachable from all branches.
+ pub fn configure_remote(
+ mut self,
+ f: impl FnMut(crate::Remote<'_>) -> Result<crate::Remote<'_>, Box<dyn std::error::Error + Send + Sync>> + 'static,
+ ) -> Self {
+ self.configure_remote = Some(Box::new(f));
+ self
+ }
+
+ /// Set the remote's name to the given value after it was configured using the function provided via
+ /// [`configure_remote()`][Self::configure_remote()].
+ ///
+ /// If not set here, it defaults to `origin` or the value of `clone.defaultRemoteName`.
+ pub fn with_remote_name(mut self, name: impl Into<BString>) -> Result<Self, crate::remote::name::Error> {
+ self.remote_name = Some(crate::remote::name::validated(name)?);
+ Ok(self)
+ }
+
+ /// Make this clone a shallow one with the respective choice of shallow-ness.
+ pub fn with_shallow(mut self, shallow: crate::remote::fetch::Shallow) -> Self {
+ self.shallow = shallow;
+ self
+ }
+}
+
+/// Consumption
+impl PrepareFetch {
+ /// Persist the contained repository as is even if an error may have occurred when fetching from the remote.
+ pub fn persist(mut self) -> Repository {
+ self.repo.take().expect("present and consumed once")
+ }
+}
+
+impl Drop for PrepareFetch {
+ fn drop(&mut self) {
+ if let Some(repo) = self.repo.take() {
+ std::fs::remove_dir_all(repo.work_dir().unwrap_or_else(|| repo.path())).ok();
+ }
+ }
+}
+
+impl From<PrepareFetch> for Repository {
+ fn from(prep: PrepareFetch) -> Self {
+ prep.persist()
+ }
+}
diff --git a/vendor/gix/src/clone/checkout.rs b/vendor/gix/src/clone/checkout.rs
index 50d235f13..823005551 100644
--- a/vendor/gix/src/clone/checkout.rs
+++ b/vendor/gix/src/clone/checkout.rs
@@ -27,8 +27,7 @@ pub mod main_worktree {
CheckoutOptions(#[from] crate::config::checkout_options::Error),
#[error(transparent)]
IndexCheckout(
- #[from]
- gix_worktree::index::checkout::Error<gix_odb::find::existing_object::Error<gix_odb::store::find::Error>>,
+ #[from] gix_worktree::checkout::Error<gix_odb::find::existing_object::Error<gix_odb::store::find::Error>>,
),
#[error("Failed to reopen object database as Arc (only if thread-safety wasn't compiled in)")]
OpenArcOdb(#[from] std::io::Error),
@@ -69,7 +68,7 @@ pub mod main_worktree {
&mut self,
mut progress: impl crate::Progress,
should_interrupt: &AtomicBool,
- ) -> Result<(Repository, gix_worktree::index::checkout::Outcome), Error> {
+ ) -> Result<(Repository, gix_worktree::checkout::Outcome), Error> {
let repo = self
.repo
.as_ref()
@@ -82,7 +81,7 @@ pub mod main_worktree {
None => {
return Ok((
self.repo.take().expect("still present"),
- gix_worktree::index::checkout::Outcome::default(),
+ gix_worktree::checkout::Outcome::default(),
))
}
};
@@ -103,7 +102,7 @@ pub mod main_worktree {
bytes.init(None, crate::progress::bytes());
let start = std::time::Instant::now();
- let outcome = gix_worktree::index::checkout(
+ let outcome = gix_worktree::checkout(
&mut index,
workdir,
{
diff --git a/vendor/gix/src/clone/fetch/mod.rs b/vendor/gix/src/clone/fetch/mod.rs
index d663b47ea..59f820675 100644
--- a/vendor/gix/src/clone/fetch/mod.rs
+++ b/vendor/gix/src/clone/fetch/mod.rs
@@ -1,9 +1,8 @@
-use crate::{bstr::BString, clone::PrepareFetch, Repository};
+use crate::clone::PrepareFetch;
/// The error returned by [`PrepareFetch::fetch_only()`].
#[derive(Debug, thiserror::Error)]
#[allow(missing_docs)]
-#[cfg(feature = "blocking-network-client")]
pub enum Error {
#[error(transparent)]
Connect(#[from] crate::remote::connect::Error),
@@ -15,6 +14,8 @@ pub enum Error {
RemoteInit(#[from] crate::remote::init::Error),
#[error("Custom configuration of remote to clone from failed")]
RemoteConfiguration(#[source] Box<dyn std::error::Error + Send + Sync>),
+ #[error("Custom configuration of connection to use when cloning failed")]
+ RemoteConnection(#[source] Box<dyn std::error::Error + Send + Sync>),
#[error(transparent)]
RemoteName(#[from] crate::config::remote::symbolic_name::Error),
#[error("Failed to load repo-local git configuration before writing")]
@@ -26,7 +27,7 @@ pub enum Error {
#[error("The remote HEAD points to a reference named {head_ref_name:?} which is invalid.")]
InvalidHeadRef {
source: gix_validate::refname::Error,
- head_ref_name: BString,
+ head_ref_name: crate::bstr::BString,
},
#[error("Failed to update HEAD with values from remote")]
HeadUpdate(#[from] crate::reference::edit::Error),
@@ -43,12 +44,11 @@ impl PrepareFetch {
/// it was newly initialized.
///
/// Note that all data we created will be removed once this instance drops if the operation wasn't successful.
- #[cfg(feature = "blocking-network-client")]
pub fn fetch_only<P>(
&mut self,
- progress: P,
+ mut progress: P,
should_interrupt: &std::sync::atomic::AtomicBool,
- ) -> Result<(Repository, crate::remote::fetch::Outcome), Error>
+ ) -> Result<(crate::Repository, crate::remote::fetch::Outcome), Error>
where
P: crate::Progress,
P::SubProgress: 'static,
@@ -100,14 +100,19 @@ impl PrepareFetch {
)
.expect("valid")
.to_owned();
- let pending_pack: remote::fetch::Prepare<'_, '_, _, _> =
- remote.connect(remote::Direction::Fetch, progress)?.prepare_fetch({
+ let pending_pack: remote::fetch::Prepare<'_, '_, _> = {
+ let mut connection = remote.connect(remote::Direction::Fetch)?;
+ if let Some(f) = self.configure_connection.as_mut() {
+ f(&mut connection).map_err(|err| Error::RemoteConnection(err))?;
+ }
+ connection.prepare_fetch(&mut progress, {
let mut opts = self.fetch_options.clone();
if !opts.extra_refspecs.contains(&head_refspec) {
opts.extra_refspecs.push(head_refspec)
}
opts
- })?;
+ })?
+ };
if pending_pack.ref_map().object_hash != repo.object_hash() {
unimplemented!("configure repository to expect a different object hash as advertised by the server")
}
@@ -121,7 +126,8 @@ impl PrepareFetch {
.with_reflog_message(RefLogMessage::Override {
message: reflog_message.clone(),
})
- .receive(should_interrupt)?;
+ .with_shallow(self.shallow.clone())
+ .receive(progress, should_interrupt)?;
util::append_config_to_repo_config(repo, config);
util::update_head(
@@ -135,7 +141,6 @@ impl PrepareFetch {
}
/// Similar to [`fetch_only()`][Self::fetch_only()`], but passes ownership to a utility type to configure a checkout operation.
- #[cfg(feature = "blocking-network-client")]
pub fn fetch_then_checkout<P>(
&mut self,
progress: P,
@@ -150,63 +155,5 @@ impl PrepareFetch {
}
}
-/// Builder
-impl PrepareFetch {
- /// Set additional options to adjust parts of the fetch operation that are not affected by the git configuration.
- #[cfg(any(feature = "async-network-client", feature = "blocking-network-client"))]
- pub fn with_fetch_options(mut self, opts: crate::remote::ref_map::Options) -> Self {
- self.fetch_options = opts;
- self
- }
- /// Use `f` to apply arbitrary changes to the remote that is about to be used to fetch a pack.
- ///
- /// The passed in `remote` will be un-named and pre-configured to be a default remote as we know it from git-clone.
- /// It is not yet present in the configuration of the repository,
- /// but each change it will eventually be written to the configuration prior to performing a the fetch operation,
- /// _all changes done in `f()` will be persisted_.
- ///
- /// It can also be used to configure additional options, like those for fetching tags. Note that
- /// [with_fetch_tags()][crate::Remote::with_fetch_tags()] should be called here to configure the clone as desired.
- /// Otherwise a clone is configured to be complete and fetches all tags, not only those reachable from all branches.
- pub fn configure_remote(
- mut self,
- f: impl FnMut(crate::Remote<'_>) -> Result<crate::Remote<'_>, Box<dyn std::error::Error + Send + Sync>> + 'static,
- ) -> Self {
- self.configure_remote = Some(Box::new(f));
- self
- }
-
- /// Set the remote's name to the given value after it was configured using the function provided via
- /// [`configure_remote()`][Self::configure_remote()].
- ///
- /// If not set here, it defaults to `origin` or the value of `clone.defaultRemoteName`.
- pub fn with_remote_name(mut self, name: impl Into<BString>) -> Result<Self, crate::remote::name::Error> {
- self.remote_name = Some(crate::remote::name::validated(name)?);
- Ok(self)
- }
-}
-
-/// Consumption
-impl PrepareFetch {
- /// Persist the contained repository as is even if an error may have occurred when fetching from the remote.
- pub fn persist(mut self) -> Repository {
- self.repo.take().expect("present and consumed once")
- }
-}
-
-impl Drop for PrepareFetch {
- fn drop(&mut self) {
- if let Some(repo) = self.repo.take() {
- std::fs::remove_dir_all(repo.work_dir().unwrap_or_else(|| repo.path())).ok();
- }
- }
-}
-
-impl From<PrepareFetch> for Repository {
- fn from(prep: PrepareFetch) -> Self {
- prep.persist()
- }
-}
-
#[cfg(feature = "blocking-network-client")]
mod util;
diff --git a/vendor/gix/src/clone/mod.rs b/vendor/gix/src/clone/mod.rs
index 249a66a42..43024e0b4 100644
--- a/vendor/gix/src/clone/mod.rs
+++ b/vendor/gix/src/clone/mod.rs
@@ -1,10 +1,16 @@
#![allow(clippy::result_large_err)]
use std::convert::TryInto;
-use crate::{bstr::BString, config::tree::gitoxide};
+use crate::{bstr::BString, config::tree::gitoxide, remote};
type ConfigureRemoteFn =
Box<dyn FnMut(crate::Remote<'_>) -> Result<crate::Remote<'_>, Box<dyn std::error::Error + Send + Sync>>>;
+#[cfg(any(feature = "async-network-client", feature = "blocking-network-client"))]
+type ConfigureConnectionFn = Box<
+ dyn FnMut(
+ &mut remote::Connection<'_, '_, Box<dyn gix_protocol::transport::client::Transport + Send>>,
+ ) -> Result<(), Box<dyn std::error::Error + Send + Sync>>,
+>;
/// A utility to collect configuration on how to fetch from a remote and initiate a fetch operation. It will delete the newly
/// created repository on when dropped without successfully finishing a fetch.
@@ -16,12 +22,18 @@ pub struct PrepareFetch {
remote_name: Option<BString>,
/// A function to configure a remote prior to fetching a pack.
configure_remote: Option<ConfigureRemoteFn>,
+ /// A function to configure a connection before using it.
+ #[cfg(any(feature = "async-network-client", feature = "blocking-network-client"))]
+ configure_connection: Option<ConfigureConnectionFn>,
/// Options for preparing a fetch operation.
#[cfg(any(feature = "async-network-client", feature = "blocking-network-client"))]
- fetch_options: crate::remote::ref_map::Options,
+ fetch_options: remote::ref_map::Options,
/// The url to clone from
#[cfg_attr(not(feature = "blocking-network-client"), allow(dead_code))]
url: gix_url::Url,
+ /// How to handle shallow clones
+ #[cfg_attr(not(feature = "blocking-network-client"), allow(dead_code))]
+ shallow: remote::fetch::Shallow,
}
/// The error returned by [`PrepareFetch::new()`].
@@ -99,6 +111,9 @@ impl PrepareFetch {
repo: Some(repo),
remote_name: None,
configure_remote: None,
+ #[cfg(any(feature = "async-network-client", feature = "blocking-network-client"))]
+ configure_connection: None,
+ shallow: remote::fetch::Shallow::NoChange,
})
}
}
@@ -111,7 +126,41 @@ pub struct PrepareCheckout {
pub(self) repo: Option<crate::Repository>,
}
+mod access;
+
+// This module encapsulates functionality that works with both feature toggles. Can be combined with `fetch`
+// once async and clone are a thing.
+#[cfg(any(feature = "async-network-client", feature = "blocking-network-client"))]
+mod access_feat {
+ use crate::clone::PrepareFetch;
+
+ /// Builder
+ impl PrepareFetch {
+ /// Set a callback to use for configuring the connection to use right before connecting to the remote.
+ ///
+ /// It is most commonly used for custom configuration.
+ // TODO: tests
+ pub fn configure_connection(
+ mut self,
+ f: impl FnMut(
+ &mut crate::remote::Connection<'_, '_, Box<dyn gix_protocol::transport::client::Transport + Send>>,
+ ) -> Result<(), Box<dyn std::error::Error + Send + Sync>>
+ + 'static,
+ ) -> Self {
+ self.configure_connection = Some(Box::new(f));
+ self
+ }
+
+ /// Set additional options to adjust parts of the fetch operation that are not affected by the git configuration.
+ pub fn with_fetch_options(mut self, opts: crate::remote::ref_map::Options) -> Self {
+ self.fetch_options = opts;
+ self
+ }
+ }
+}
+
///
+#[cfg(feature = "blocking-network-client")]
pub mod fetch;
///
diff --git a/vendor/gix/src/commit.rs b/vendor/gix/src/commit.rs
index 10fa6f675..a58954a36 100644
--- a/vendor/gix/src/commit.rs
+++ b/vendor/gix/src/commit.rs
@@ -62,9 +62,10 @@ pub mod describe {
}
/// A selector to choose what kind of references should contribute to names.
- #[derive(Debug, Clone, Copy, PartialOrd, PartialEq, Ord, Eq, Hash)]
+ #[derive(Default, Debug, Clone, Copy, PartialOrd, PartialEq, Ord, Eq, Hash)]
pub enum SelectRef {
/// Only use annotated tags for names.
+ #[default]
AnnotatedTags,
/// Use all tags for names, annotated or plain reference.
AllTags,
@@ -146,12 +147,6 @@ pub mod describe {
}
}
- impl Default for SelectRef {
- fn default() -> Self {
- SelectRef::AnnotatedTags
- }
- }
-
/// A support type to allow configuring a `git describe` operation
pub struct Platform<'repo> {
pub(crate) id: gix_hash::ObjectId,
diff --git a/vendor/gix/src/config/cache/access.rs b/vendor/gix/src/config/cache/access.rs
index 8244eaf27..77324efe3 100644
--- a/vendor/gix/src/config/cache/access.rs
+++ b/vendor/gix/src/config/cache/access.rs
@@ -1,6 +1,7 @@
#![allow(clippy::result_large_err)]
use std::{borrow::Cow, path::PathBuf, time::Duration};
+use gix_attributes::Source;
use gix_lock::acquire::Fail;
use crate::{
@@ -9,7 +10,7 @@ use crate::{
config::{
cache::util::{ApplyLeniency, ApplyLeniencyDefault},
checkout_options,
- tree::{Checkout, Core, Key},
+ tree::{gitoxide, Checkout, Core, Key},
Cache,
},
remote,
@@ -137,7 +138,7 @@ impl Cache {
pub(crate) fn checkout_options(
&self,
git_dir: &std::path::Path,
- ) -> Result<gix_worktree::index::checkout::Options, checkout_options::Error> {
+ ) -> Result<gix_worktree::checkout::Options, checkout_options::Error> {
fn boolean(
me: &Cache,
full_key: &str,
@@ -154,63 +155,118 @@ impl Cache {
.unwrap_or(default))
}
- fn assemble_attribute_globals(
- me: &Cache,
- _git_dir: &std::path::Path,
- ) -> Result<gix_attributes::MatchGroup, checkout_options::Error> {
- let _attributes_file = match me
- .trusted_file_path("core", None, Core::ATTRIBUTES_FILE.name)
- .transpose()?
- {
- Some(attributes) => Some(attributes.into_owned()),
- None => me.xdg_config_path("attributes").ok().flatten(),
- };
- // TODO: implement gix_attributes::MatchGroup::<gix_attributes::Attributes>::from_git_dir(), similar to what's done for `Ignore`.
- Ok(Default::default())
- }
-
let thread_limit = self.apply_leniency(
self.resolved
.integer_filter_by_key("checkout.workers", &mut self.filter_config_section.clone())
.map(|value| Checkout::WORKERS.try_from_workers(value)),
)?;
- Ok(gix_worktree::index::checkout::Options {
- fs: gix_worktree::fs::Capabilities {
- precompose_unicode: boolean(self, "core.precomposeUnicode", &Core::PRECOMPOSE_UNICODE, false)?,
- ignore_case: boolean(self, "core.ignoreCase", &Core::IGNORE_CASE, false)?,
- executable_bit: boolean(self, "core.fileMode", &Core::FILE_MODE, true)?,
- symlink: boolean(self, "core.symlinks", &Core::SYMLINKS, true)?,
- },
+ let capabilities = gix_fs::Capabilities {
+ precompose_unicode: boolean(self, "core.precomposeUnicode", &Core::PRECOMPOSE_UNICODE, false)?,
+ ignore_case: boolean(self, "core.ignoreCase", &Core::IGNORE_CASE, false)?,
+ executable_bit: boolean(self, "core.fileMode", &Core::FILE_MODE, true)?,
+ symlink: boolean(self, "core.symlinks", &Core::SYMLINKS, true)?,
+ };
+ Ok(gix_worktree::checkout::Options {
+ attributes: self
+ .assemble_attribute_globals(
+ git_dir,
+ gix_worktree::cache::state::attributes::Source::IdMappingThenWorktree,
+ self.attributes,
+ )?
+ .0,
+ fs: capabilities,
thread_limit,
destination_is_initially_empty: false,
overwrite_existing: false,
keep_going: false,
- trust_ctime: boolean(self, "core.trustCTime", &Core::TRUST_C_TIME, true)?,
- check_stat: self
- .apply_leniency(
- self.resolved
- .string("core", None, "checkStat")
- .map(|v| Core::CHECK_STAT.try_into_checkstat(v)),
- )?
- .unwrap_or(true),
- attribute_globals: assemble_attribute_globals(self, git_dir)?,
+ stat_options: gix_index::entry::stat::Options {
+ trust_ctime: boolean(self, "core.trustCTime", &Core::TRUST_C_TIME, true)?,
+ use_nsec: boolean(self, "gitoxide.core.useNsec", &gitoxide::Core::USE_NSEC, false)?,
+ use_stdev: boolean(self, "gitoxide.core.useStdev", &gitoxide::Core::USE_STDEV, false)?,
+ check_stat: self
+ .apply_leniency(
+ self.resolved
+ .string("core", None, "checkStat")
+ .map(|v| Core::CHECK_STAT.try_into_checkstat(v)),
+ )?
+ .unwrap_or(true),
+ },
})
}
+
+ pub(crate) fn assemble_exclude_globals(
+ &self,
+ git_dir: &std::path::Path,
+ overrides: Option<gix_ignore::Search>,
+ source: gix_worktree::cache::state::ignore::Source,
+ buf: &mut Vec<u8>,
+ ) -> Result<gix_worktree::cache::state::Ignore, config::exclude_stack::Error> {
+ let excludes_file = match self.excludes_file().transpose()? {
+ Some(user_path) => Some(user_path),
+ None => self.xdg_config_path("ignore")?,
+ };
+ Ok(gix_worktree::cache::state::Ignore::new(
+ overrides.unwrap_or_default(),
+ gix_ignore::Search::from_git_dir(git_dir, excludes_file, buf)?,
+ None,
+ source,
+ ))
+ }
+ // TODO: at least one test, maybe related to core.attributesFile configuration.
+ pub(crate) fn assemble_attribute_globals(
+ &self,
+ git_dir: &std::path::Path,
+ source: gix_worktree::cache::state::attributes::Source,
+ attributes: crate::open::permissions::Attributes,
+ ) -> Result<(gix_worktree::cache::state::Attributes, Vec<u8>), config::attribute_stack::Error> {
+ let configured_or_user_attributes = match self
+ .trusted_file_path("core", None, Core::ATTRIBUTES_FILE.name)
+ .transpose()?
+ {
+ Some(attributes) => Some(attributes),
+ None => {
+ if attributes.git {
+ self.xdg_config_path("attributes").ok().flatten().map(Cow::Owned)
+ } else {
+ None
+ }
+ }
+ };
+ let attribute_files = [gix_attributes::Source::GitInstallation, gix_attributes::Source::System]
+ .into_iter()
+ .filter(|source| match source {
+ Source::GitInstallation => attributes.git_binary,
+ Source::System => attributes.system,
+ Source::Git | Source::Local => unreachable!("we don't offer turning this off right now"),
+ })
+ .filter_map(|source| source.storage_location(&mut Self::make_source_env(self.environment)))
+ .chain(configured_or_user_attributes);
+ let info_attributes_path = git_dir.join("info").join("attributes");
+ let mut buf = Vec::new();
+ let mut collection = gix_attributes::search::MetadataCollection::default();
+ let res = gix_worktree::cache::state::Attributes::new(
+ gix_attributes::Search::new_globals(attribute_files, &mut buf, &mut collection)?,
+ Some(info_attributes_path),
+ source,
+ collection,
+ );
+ Ok((res, buf))
+ }
+
pub(crate) fn xdg_config_path(
&self,
resource_file_name: &str,
) -> Result<Option<PathBuf>, gix_sec::permission::Error<PathBuf>> {
std::env::var_os("XDG_CONFIG_HOME")
- .map(|path| (PathBuf::from(path), &self.xdg_config_home_env))
+ .map(|path| (PathBuf::from(path), &self.environment.xdg_config_home))
.or_else(|| {
- std::env::var_os("HOME").map(|path| {
+ gix_path::env::home_dir().map(|mut p| {
(
{
- let mut p = PathBuf::from(path);
p.push(".config");
p
},
- &self.home_env,
+ &self.environment.home,
)
})
})
@@ -226,8 +282,6 @@ impl Cache {
/// We never fail for here even if the permission is set to deny as we `gix-config` will fail later
/// if it actually wants to use the home directory - we don't want to fail prematurely.
pub(crate) fn home_dir(&self) -> Option<PathBuf> {
- std::env::var_os("HOME")
- .map(PathBuf::from)
- .and_then(|path| self.home_env.check_opt(path))
+ gix_path::env::home_dir().and_then(|path| self.environment.home.check_opt(path))
}
}
diff --git a/vendor/gix/src/config/cache/incubate.rs b/vendor/gix/src/config/cache/incubate.rs
index 047f2132b..44c537b50 100644
--- a/vendor/gix/src/config/cache/incubate.rs
+++ b/vendor/gix/src/config/cache/incubate.rs
@@ -30,6 +30,7 @@ impl StageOne {
gix_config::Source::Local,
git_dir_trust,
lossy,
+ lenient,
)?;
// Note that we assume the repo is bare by default unless we are told otherwise. This is relevant if
@@ -64,6 +65,7 @@ impl StageOne {
gix_config::Source::Worktree,
git_dir_trust,
lossy,
+ lenient,
)?;
config.append(worktree_config);
};
@@ -86,24 +88,48 @@ fn load_config(
source: gix_config::Source,
git_dir_trust: gix_sec::Trust,
lossy: Option<bool>,
+ lenient: bool,
) -> Result<gix_config::File<'static>, Error> {
- buf.clear();
let metadata = gix_config::file::Metadata::from(source)
.at(&config_path)
.with(git_dir_trust);
let mut file = match std::fs::File::open(&config_path) {
Ok(f) => f,
Err(err) if err.kind() == std::io::ErrorKind::NotFound => return Ok(gix_config::File::new(metadata)),
- Err(err) => return Err(err.into()),
+ Err(err) => {
+ let err = Error::Io {
+ source: err,
+ path: config_path,
+ };
+ if lenient {
+ log::warn!("ignoring: {err:#?}");
+ return Ok(gix_config::File::new(metadata));
+ } else {
+ return Err(err);
+ }
+ }
+ };
+
+ buf.clear();
+ if let Err(err) = std::io::copy(&mut file, buf) {
+ let err = Error::Io {
+ source: err,
+ path: config_path,
+ };
+ if lenient {
+ log::warn!("ignoring: {err:#?}");
+ buf.clear();
+ } else {
+ return Err(err);
+ }
};
- std::io::copy(&mut file, buf)?;
let config = gix_config::File::from_bytes_owned(
buf,
metadata,
gix_config::file::init::Options {
includes: gix_config::file::includes::Options::no_follow(),
- ..util::base_options(lossy)
+ ..util::base_options(lossy, lenient)
},
)?;
diff --git a/vendor/gix/src/config/cache/init.rs b/vendor/gix/src/config/cache/init.rs
index dc76f78bb..ee20e0354 100644
--- a/vendor/gix/src/config/cache/init.rs
+++ b/vendor/gix/src/config/cache/init.rs
@@ -1,5 +1,5 @@
#![allow(clippy::result_large_err)]
-use std::borrow::Cow;
+use std::{borrow::Cow, ffi::OsString};
use gix_sec::Permission;
@@ -12,7 +12,7 @@ use crate::{
tree::{gitoxide, Core, Http},
Cache,
},
- repository,
+ open,
};
/// Initialization
@@ -32,23 +32,24 @@ impl Cache {
filter_config_section: fn(&gix_config::file::Metadata) -> bool,
git_install_dir: Option<&std::path::Path>,
home: Option<&std::path::Path>,
- repository::permissions::Environment {
+ environment @ open::permissions::Environment {
git_prefix,
- home: home_env,
- xdg_config_home: xdg_config_home_env,
ssh_prefix: _,
+ xdg_config_home: _,
+ home: _,
http_transport,
identity,
objects,
- }: repository::permissions::Environment,
- repository::permissions::Config {
+ }: open::permissions::Environment,
+ attributes: open::permissions::Attributes,
+ open::permissions::Config {
git_binary: use_installation,
system: use_system,
git: use_git,
user: use_user,
env: use_env,
includes: use_includes,
- }: repository::permissions::Config,
+ }: open::permissions::Config,
lenient_config: bool,
api_config_overrides: &[BString],
cli_config_overrides: &[BString],
@@ -65,12 +66,10 @@ impl Cache {
} else {
gix_config::file::includes::Options::no_follow()
},
- ..util::base_options(lossy)
+ ..util::base_options(lossy, lenient_config)
};
let config = {
- let home_env = &home_env;
- let xdg_config_home_env = &xdg_config_home_env;
let git_prefix = &git_prefix;
let metas = [
gix_config::source::Kind::GitInstallation,
@@ -88,15 +87,7 @@ impl Cache {
_ => {}
}
source
- .storage_location(&mut |name| {
- match name {
- git_ if git_.starts_with("GIT_") => Some(git_prefix),
- "XDG_CONFIG_HOME" => Some(xdg_config_home_env),
- "HOME" => Some(home_env),
- _ => None,
- }
- .and_then(|perm| perm.check_opt(name).and_then(std::env::var_os))
- })
+ .storage_location(&mut Self::make_source_env(environment))
.map(|p| (source, p.into_owned()))
})
.map(|(source, path)| gix_config::file::Metadata {
@@ -118,7 +109,7 @@ impl Cache {
)
.map_err(|err| match err {
gix_config::file::init::from_paths::Error::Init(err) => Error::from(err),
- gix_config::file::init::from_paths::Error::Io(err) => err.into(),
+ gix_config::file::init::from_paths::Error::Io { source, path } => Error::Io { source, path },
})?
.unwrap_or_default();
@@ -175,9 +166,9 @@ impl Cache {
ignore_case,
hex_len,
filter_config_section,
- xdg_config_home_env,
- home_env,
+ environment,
lenient_config,
+ attributes,
user_agent: Default::default(),
personas: Default::default(),
url_rewrite: Default::default(),
@@ -240,6 +231,31 @@ impl Cache {
Ok(())
}
+
+ pub(crate) fn make_source_env(
+ crate::open::permissions::Environment {
+ xdg_config_home,
+ git_prefix,
+ home,
+ ..
+ }: open::permissions::Environment,
+ ) -> impl FnMut(&str) -> Option<OsString> {
+ move |name| {
+ match name {
+ git_ if git_.starts_with("GIT_") => Some(git_prefix),
+ "XDG_CONFIG_HOME" => Some(xdg_config_home),
+ "HOME" => {
+ return if home.is_allowed() {
+ gix_path::env::home_dir().map(Into::into)
+ } else {
+ None
+ }
+ }
+ _ => None,
+ }
+ .and_then(|perm| perm.check_opt(name).and_then(gix_path::env::var))
+ }
+ }
}
impl crate::Repository {
@@ -349,6 +365,15 @@ fn apply_environment_overrides(
),
(
"gitoxide",
+ Some(Cow::Borrowed("core".into())),
+ git_prefix,
+ &[{
+ let key = &gitoxide::Core::SHALLOW_FILE;
+ (env(key), key.name)
+ }],
+ ),
+ (
+ "gitoxide",
Some(Cow::Borrowed("author".into())),
identity,
&[
diff --git a/vendor/gix/src/config/cache/util.rs b/vendor/gix/src/config/cache/util.rs
index c12f850e6..d5a0a4acb 100644
--- a/vendor/gix/src/config/cache/util.rs
+++ b/vendor/gix/src/config/cache/util.rs
@@ -17,9 +17,10 @@ pub(crate) fn interpolate_context<'a>(
}
}
-pub(crate) fn base_options(lossy: Option<bool>) -> gix_config::file::init::Options<'static> {
+pub(crate) fn base_options(lossy: Option<bool>, lenient: bool) -> gix_config::file::init::Options<'static> {
gix_config::file::init::Options {
lossy: lossy.unwrap_or(!cfg!(debug_assertions)),
+ ignore_io_errors: lenient,
..Default::default()
}
}
diff --git a/vendor/gix/src/config/mod.rs b/vendor/gix/src/config/mod.rs
index 1e2566777..5da569605 100644
--- a/vendor/gix/src/config/mod.rs
+++ b/vendor/gix/src/config/mod.rs
@@ -62,8 +62,11 @@ pub enum Error {
UnsupportedObjectFormat { name: BString },
#[error(transparent)]
CoreAbbrev(#[from] abbrev::Error),
- #[error("Could not read configuration file")]
- Io(#[from] std::io::Error),
+ #[error("Could not read configuration file at \"{}\"", path.display())]
+ Io {
+ source: std::io::Error,
+ path: std::path::PathBuf,
+ },
#[error(transparent)]
Init(#[from] gix_config::file::init::Error),
#[error(transparent)]
@@ -110,6 +113,36 @@ pub mod checkout_options {
ConfigBoolean(#[from] super::boolean::Error),
#[error(transparent)]
CheckoutWorkers(#[from] super::checkout::workers::Error),
+ #[error(transparent)]
+ Attributes(#[from] super::attribute_stack::Error),
+ }
+}
+
+///
+pub mod exclude_stack {
+ use std::path::PathBuf;
+
+ /// The error produced when setting up a stack to query `gitignore` information.
+ #[derive(Debug, thiserror::Error)]
+ #[allow(missing_docs)]
+ pub enum Error {
+ #[error("Could not read repository exclude")]
+ Io(#[from] std::io::Error),
+ #[error(transparent)]
+ EnvironmentPermission(#[from] gix_sec::permission::Error<PathBuf>),
+ #[error("The value for `core.excludesFile` could not be read from configuration")]
+ ExcludesFilePathInterpolation(#[from] gix_config::path::interpolate::Error),
+ }
+}
+
+///
+pub mod attribute_stack {
+ /// The error produced when setting up the attribute stack to query `gitattributes`.
+ #[derive(Debug, thiserror::Error)]
+ #[allow(missing_docs)]
+ pub enum Error {
+ #[error("An attribute file could not be read")]
+ Io(#[from] std::io::Error),
#[error("Failed to interpolate the attribute file configured at `core.attributesFile`")]
AttributesFileInterpolation(#[from] gix_config::path::interpolate::Error),
}
@@ -247,7 +280,7 @@ pub mod checkout {
pub mod workers {
use crate::config;
- /// The error produced when failing to parse the the `checkout.workers` key.
+ /// The error produced when failing to parse the `checkout.workers` key.
pub type Error = config::key::Error<gix_config::value::Error, 'n', 'd'>;
}
}
@@ -446,9 +479,7 @@ pub(crate) struct Cache {
/// If true, we should default what's possible if something is misconfigured, on case by case basis, to be more resilient.
/// Also available in options! Keep in sync!
pub lenient_config: bool,
- /// Define how we can use values obtained with `xdg_config(…)` and its `XDG_CONFIG_HOME` variable.
- xdg_config_home_env: gix_sec::Permission,
- /// Define how we can use values obtained with `xdg_config(…)`. and its `HOME` variable.
- home_env: gix_sec::Permission,
+ attributes: crate::open::permissions::Attributes,
+ environment: crate::open::permissions::Environment,
// TODO: make core.precomposeUnicode available as well.
}
diff --git a/vendor/gix/src/config/tree/mod.rs b/vendor/gix/src/config/tree/mod.rs
index fd769f3ed..b378b8c49 100644
--- a/vendor/gix/src/config/tree/mod.rs
+++ b/vendor/gix/src/config/tree/mod.rs
@@ -38,6 +38,8 @@ pub(crate) mod root {
pub const GITOXIDE: sections::Gitoxide = sections::Gitoxide;
/// The `http` section.
pub const HTTP: sections::Http = sections::Http;
+ /// The `index` section.
+ pub const INDEX: sections::Index = sections::Index;
/// The `init` section.
pub const INIT: sections::Init = sections::Init;
/// The `pack` section.
@@ -69,6 +71,7 @@ pub(crate) mod root {
&Self::EXTENSIONS,
&Self::GITOXIDE,
&Self::HTTP,
+ &Self::INDEX,
&Self::INIT,
&Self::PACK,
&Self::PROTOCOL,
@@ -84,9 +87,9 @@ pub(crate) mod root {
mod sections;
pub use sections::{
- branch, checkout, core, credential, diff, extensions, gitoxide, http, protocol, remote, ssh, Author, Branch,
- Checkout, Clone, Committer, Core, Credential, Diff, Extensions, Gitoxide, Http, Init, Pack, Protocol, Remote, Safe,
- Ssh, Url, User,
+ branch, checkout, core, credential, diff, extensions, gitoxide, http, index, protocol, remote, ssh, Author, Branch,
+ Checkout, Clone, Committer, Core, Credential, Diff, Extensions, Gitoxide, Http, Index, Init, Pack, Protocol,
+ Remote, Safe, Ssh, Url, User,
};
/// Generic value implementations for static instantiation.
diff --git a/vendor/gix/src/config/tree/sections/clone.rs b/vendor/gix/src/config/tree/sections/clone.rs
index 616185a0b..6cb274e7d 100644
--- a/vendor/gix/src/config/tree/sections/clone.rs
+++ b/vendor/gix/src/config/tree/sections/clone.rs
@@ -7,6 +7,8 @@ impl Clone {
/// The `clone.defaultRemoteName` key.
pub const DEFAULT_REMOTE_NAME: keys::RemoteName =
keys::RemoteName::new_remote_name("defaultRemoteName", &config::Tree::CLONE);
+ /// The `clone.rejectShallow` key.
+ pub const REJECT_SHALLOW: keys::Boolean = keys::Boolean::new_boolean("rejectShallow", &config::Tree::CLONE);
}
impl Section for Clone {
@@ -15,6 +17,6 @@ impl Section for Clone {
}
fn keys(&self) -> &[&dyn Key] {
- &[&Self::DEFAULT_REMOTE_NAME]
+ &[&Self::DEFAULT_REMOTE_NAME, &Self::REJECT_SHALLOW]
}
}
diff --git a/vendor/gix/src/config/tree/sections/gitoxide.rs b/vendor/gix/src/config/tree/sections/gitoxide.rs
index 8c3defd0b..7d60f1287 100644
--- a/vendor/gix/src/config/tree/sections/gitoxide.rs
+++ b/vendor/gix/src/config/tree/sections/gitoxide.rs
@@ -1,10 +1,15 @@
-use crate::config::tree::{keys, Gitoxide, Key, Section};
+use crate::{
+ config,
+ config::tree::{keys, Gitoxide, Key, Section},
+};
impl Gitoxide {
/// The `gitoxide.allow` section.
pub const ALLOW: Allow = Allow;
/// The `gitoxide.author` section.
pub const AUTHOR: Author = Author;
+ /// The `gitoxide.core` section.
+ pub const CORE: Core = Core;
/// The `gitoxide.commit` section.
pub const COMMIT: Commit = Commit;
/// The `gitoxide.committer` section.
@@ -39,6 +44,7 @@ impl Section for Gitoxide {
&[
&Self::ALLOW,
&Self::AUTHOR,
+ &Self::CORE,
&Self::COMMIT,
&Self::COMMITTER,
&Self::HTTP,
@@ -56,6 +62,41 @@ mod subsections {
Tree,
};
+ /// The `Core` sub-section.
+ #[derive(Copy, Clone, Default)]
+ pub struct Core;
+
+ impl Core {
+ /// The `gitoxide.core.useNsec` key.
+ pub const USE_NSEC: keys::Boolean = keys::Boolean::new_boolean("useNsec", &Gitoxide::CORE)
+ .with_note("A runtime version of the USE_NSEC build flag.");
+
+ /// The `gitoxide.core.useStdev` key.
+ pub const USE_STDEV: keys::Boolean = keys::Boolean::new_boolean("useStdev", &Gitoxide::CORE)
+ .with_note("A runtime version of the USE_STDEV build flag.");
+
+ /// The `gitoxide.core.shallowFile` key.
+ pub const SHALLOW_FILE: keys::Path = keys::Path::new_path("shallowFile", &Gitoxide::CORE)
+ .with_environment_override("GIT_SHALLOW_FILE")
+ .with_deviation(
+ "relative file paths will always be made relative to the git-common-dir, whereas `git` keeps them as is.",
+ );
+ }
+
+ impl Section for Core {
+ fn name(&self) -> &str {
+ "core"
+ }
+
+ fn keys(&self) -> &[&dyn Key] {
+ &[&Self::USE_NSEC, &Self::USE_STDEV, &Self::SHALLOW_FILE]
+ }
+
+ fn parent(&self) -> Option<&dyn Section> {
+ Some(&Tree::GITOXIDE)
+ }
+ }
+
/// The `Http` sub-section.
#[derive(Copy, Clone, Default)]
pub struct Http;
@@ -341,6 +382,7 @@ mod subsections {
}
}
}
+pub use subsections::{Allow, Author, Commit, Committer, Core, Http, Https, Objects, Ssh, User};
pub mod validate {
use std::error::Error;
@@ -357,7 +399,3 @@ pub mod validate {
}
}
}
-
-pub use subsections::{Allow, Author, Commit, Committer, Http, Https, Objects, Ssh, User};
-
-use crate::config;
diff --git a/vendor/gix/src/config/tree/sections/index.rs b/vendor/gix/src/config/tree/sections/index.rs
new file mode 100644
index 000000000..d03322247
--- /dev/null
+++ b/vendor/gix/src/config/tree/sections/index.rs
@@ -0,0 +1,62 @@
+use crate::{
+ config,
+ config::tree::{keys, Index, Key, Section},
+};
+
+impl Index {
+ /// The `index.threads` key.
+ pub const THREADS: IndexThreads =
+ IndexThreads::new_with_validate("threads", &config::Tree::INDEX, validate::IndexThreads);
+}
+
+/// The `index.threads` key.
+pub type IndexThreads = keys::Any<validate::IndexThreads>;
+
+mod index_threads {
+ use crate::bstr::BStr;
+ use crate::config;
+ use crate::config::key::GenericErrorWithValue;
+ use crate::config::tree::index::IndexThreads;
+ use std::borrow::Cow;
+
+ impl IndexThreads {
+ /// Parse `value` into the amount of threads to use, with `1` being single-threaded, or `0` indicating
+ /// to select the amount of threads, with any other number being the specific amount of threads to use.
+ pub fn try_into_index_threads(
+ &'static self,
+ value: Cow<'_, BStr>,
+ ) -> Result<usize, config::key::GenericErrorWithValue> {
+ gix_config::Integer::try_from(value.as_ref())
+ .ok()
+ .and_then(|i| i.to_decimal().and_then(|i| i.try_into().ok()))
+ .or_else(|| {
+ gix_config::Boolean::try_from(value.as_ref())
+ .ok()
+ .map(|b| if b.0 { 0 } else { 1 })
+ })
+ .ok_or_else(|| GenericErrorWithValue::from_value(self, value.into_owned()))
+ }
+ }
+}
+
+impl Section for Index {
+ fn name(&self) -> &str {
+ "index"
+ }
+
+ fn keys(&self) -> &[&dyn Key] {
+ &[&Self::THREADS]
+ }
+}
+
+mod validate {
+ use crate::{bstr::BStr, config::tree::keys};
+
+ pub struct IndexThreads;
+ impl keys::Validate for IndexThreads {
+ fn validate(&self, value: &BStr) -> Result<(), Box<dyn std::error::Error + Send + Sync + 'static>> {
+ super::Index::THREADS.try_into_index_threads(value.into())?;
+ Ok(())
+ }
+ }
+}
diff --git a/vendor/gix/src/config/tree/sections/mod.rs b/vendor/gix/src/config/tree/sections/mod.rs
index fb9b50786..9f0a50c93 100644
--- a/vendor/gix/src/config/tree/sections/mod.rs
+++ b/vendor/gix/src/config/tree/sections/mod.rs
@@ -55,6 +55,11 @@ pub mod gitoxide;
pub struct Http;
pub mod http;
+/// The `index` top-level section.
+#[derive(Copy, Clone, Default)]
+pub struct Index;
+pub mod index;
+
/// The `init` top-level section.
#[derive(Copy, Clone, Default)]
pub struct Init;
diff --git a/vendor/gix/src/create.rs b/vendor/gix/src/create.rs
index 96d047e3b..878ec1164 100644
--- a/vendor/gix/src/create.rs
+++ b/vendor/gix/src/create.rs
@@ -117,7 +117,7 @@ pub struct Options {
pub destination_must_be_empty: bool,
/// If set, use these filesystem capabilities to populate the respective gix-config fields.
/// If `None`, the directory will be probed.
- pub fs_capabilities: Option<gix_worktree::fs::Capabilities>,
+ pub fs_capabilities: Option<gix_fs::Capabilities>,
}
/// Create a new `.git` repository of `kind` within the possibly non-existing `directory`
@@ -208,7 +208,7 @@ pub fn into(
{
let mut config = gix_config::File::default();
{
- let caps = fs_capabilities.unwrap_or_else(|| gix_worktree::fs::Capabilities::probe(&dot_git));
+ let caps = fs_capabilities.unwrap_or_else(|| gix_fs::Capabilities::probe(&dot_git));
let mut core = config.new_section("core", None).expect("valid section name");
core.push(key("repositoryformatversion"), Some("0".into()));
diff --git a/vendor/gix/src/diff.rs b/vendor/gix/src/diff.rs
new file mode 100644
index 000000000..b10819293
--- /dev/null
+++ b/vendor/gix/src/diff.rs
@@ -0,0 +1,17 @@
+pub use gix_diff::*;
+
+///
+pub mod rename {
+ /// Determine how to do rename tracking.
+ #[derive(Debug, Copy, Clone, Eq, PartialEq)]
+ pub enum Tracking {
+ /// Do not track renames at all, the fastest option.
+ Disabled,
+ /// Track renames.
+ Renames,
+ /// Track renames and copies.
+ ///
+ /// This is the most expensive option.
+ RenamesAndCopies,
+ }
+}
diff --git a/vendor/gix/src/id.rs b/vendor/gix/src/id.rs
index c57565fb5..72f424334 100644
--- a/vendor/gix/src/id.rs
+++ b/vendor/gix/src/id.rs
@@ -90,9 +90,6 @@ impl<'repo> Id<'repo> {
impl<'repo> Id<'repo> {
/// Obtain a platform for traversing ancestors of this commit.
///
- /// Note that unless [`error_on_missing_commit()`][revision::Walk::error_on_missing_commit()] is enabled, which be default it is not,
- /// one will always see an empty iteration even if this id is not a commit, instead of an error.
- /// If this is undesirable, it's best to check for the correct object type before creating an iterator.
pub fn ancestors(&self) -> revision::walk::Platform<'repo> {
revision::walk::Platform::new(Some(self.inner), self.repo)
}
@@ -186,10 +183,11 @@ mod tests {
#[test]
fn size_of_oid() {
- assert_eq!(
- std::mem::size_of::<Id<'_>>(),
- 32,
- "size of oid shouldn't change without notice"
+ let actual = std::mem::size_of::<Id<'_>>();
+ let ceiling = 32;
+ assert!(
+ actual <= ceiling,
+ "size of oid shouldn't change without notice: {actual} <= {ceiling}"
)
}
}
diff --git a/vendor/gix/src/lib.rs b/vendor/gix/src/lib.rs
index 257a613d7..eb5efcfdf 100644
--- a/vendor/gix/src/lib.rs
+++ b/vendor/gix/src/lib.rs
@@ -73,8 +73,10 @@ pub use gix_date as date;
pub use gix_features as features;
use gix_features::threading::OwnShared;
pub use gix_features::{parallel, progress::Progress, threading};
+pub use gix_fs as fs;
pub use gix_glob as glob;
pub use gix_hash as hash;
+pub use gix_ignore as ignore;
#[doc(inline)]
pub use gix_index as index;
pub use gix_lock as lock;
@@ -92,18 +94,17 @@ pub use gix_traverse as traverse;
pub use gix_url as url;
#[doc(inline)]
pub use gix_url::Url;
+pub use gix_utils as utils;
pub use hash::{oid, ObjectId};
pub mod interrupt;
-mod ext;
///
-pub mod prelude {
- pub use gix_features::parallel::reduce::Finalize;
- pub use gix_odb::{Find, FindExt, Header, HeaderExt, Write};
+pub mod attributes;
- pub use crate::ext::*;
-}
+mod ext;
+///
+pub mod prelude;
///
pub mod path;
@@ -133,33 +134,21 @@ mod repository;
pub mod tag;
///
-pub mod progress {
- #[cfg(feature = "progress-tree")]
- pub use gix_features::progress::prodash::tree;
- pub use gix_features::progress::*;
-}
+pub mod progress;
///
-pub mod diff {
- pub use gix_diff::*;
- ///
- pub mod rename {
- /// Determine how to do rename tracking.
- #[derive(Debug, Copy, Clone, Eq, PartialEq)]
- pub enum Tracking {
- /// Do not track renames at all, the fastest option.
- Disabled,
- /// Track renames.
- Renames,
- /// Track renames and copies.
- ///
- /// This is the most expensive option.
- RenamesAndCopies,
- }
- }
-}
+pub mod diff;
/// See [ThreadSafeRepository::discover()], but returns a [`Repository`] instead.
+///
+/// # Note
+///
+/// **The discovered repository might not be suitable for any operation that requires authentication with remotes**
+/// as it doesn't see the relevant git configuration.
+///
+/// To achieve that, one has to [enable `git_binary` configuration](https://github.com/Byron/gitoxide/blob/9723e1addf52cc336d59322de039ea0537cdca36/src/plumbing/main.rs#L86)
+/// in the open-options and use [`ThreadSafeRepository::discover_opts()`] instead. Alternatively, it might be well-known
+/// that the tool is going to run in a neatly configured environment without relying on bundled configuration.
#[allow(clippy::result_large_err)]
pub fn discover(directory: impl AsRef<std::path::Path>) -> Result<Repository, discover::Error> {
ThreadSafeRepository::discover(directory).map(Into::into)
@@ -238,23 +227,6 @@ pub fn open_opts(directory: impl Into<std::path::PathBuf>, options: open::Option
}
///
-pub mod permission {
- ///
- pub mod env_var {
- ///
- pub mod resource {
- ///
- pub type Error = gix_sec::permission::Error<std::path::PathBuf>;
- }
- }
-}
-///
-pub mod permissions {
- pub use crate::repository::permissions::{Config, Environment};
-}
-pub use repository::permissions::Permissions;
-
-///
pub mod create;
///
@@ -278,33 +250,10 @@ pub mod remote;
pub mod init;
/// Not to be confused with 'status'.
-pub mod state {
- /// Tell what operation is currently in progress.
- #[derive(Debug, PartialEq, Eq)]
- pub enum InProgress {
- /// A mailbox is being applied.
- ApplyMailbox,
- /// A rebase is happening while a mailbox is being applied.
- // TODO: test
- ApplyMailboxRebase,
- /// A git bisect operation has not yet been concluded.
- Bisect,
- /// A cherry pick operation.
- CherryPick,
- /// A cherry pick with multiple commits pending.
- CherryPickSequence,
- /// A merge operation.
- Merge,
- /// A rebase operation.
- Rebase,
- /// An interactive rebase operation.
- RebaseInteractive,
- /// A revert operation.
- Revert,
- /// A revert operation with multiple commits pending.
- RevertSequence,
- }
-}
+pub mod state;
+
+///
+pub mod shallow;
///
pub mod discover;
diff --git a/vendor/gix/src/object/commit.rs b/vendor/gix/src/object/commit.rs
index e28a12955..5a9dfd4f3 100644
--- a/vendor/gix/src/object/commit.rs
+++ b/vendor/gix/src/object/commit.rs
@@ -109,7 +109,7 @@ impl<'repo> Commit<'repo> {
.map(move |id| id.attach(repo))
}
- /// Parse the commit and return the the tree object it points to.
+ /// Parse the commit and return the tree object it points to.
pub fn tree(&self) -> Result<Tree<'repo>, Error> {
match self.tree_id()?.object()?.try_into_tree() {
Ok(tree) => Ok(tree),
@@ -117,7 +117,7 @@ impl<'repo> Commit<'repo> {
}
}
- /// Parse the commit and return the the tree id it points to.
+ /// Parse the commit and return the tree id it points to.
pub fn tree_id(&self) -> Result<crate::Id<'repo>, gix_object::decode::Error> {
gix_object::CommitRefIter::from_bytes(&self.data)
.tree_id()
diff --git a/vendor/gix/src/object/mod.rs b/vendor/gix/src/object/mod.rs
index 75d77d138..d0a37db6c 100644
--- a/vendor/gix/src/object/mod.rs
+++ b/vendor/gix/src/object/mod.rs
@@ -90,6 +90,14 @@ impl<'repo> Object<'repo> {
}
}
+ /// Transform this object into a tag, or panic if it is none.
+ pub fn into_tag(self) -> Tag<'repo> {
+ match self.try_into() {
+ Ok(tag) => tag,
+ Err(this) => panic!("Tried to use {} as commit, but was {}", this.id, this.kind),
+ }
+ }
+
/// Transform this object into a commit, or return it as part of the `Err` if it is no commit.
pub fn try_into_commit(self) -> Result<Commit<'repo>, try_into::Error> {
self.try_into().map_err(|this: Self| try_into::Error {
@@ -157,7 +165,7 @@ impl<'repo> Object<'repo> {
})
}
- /// Obtain a an iterator over commit tokens like in [`to_commit_iter()`][Object::try_to_commit_ref_iter()].
+ /// Obtain an iterator over commit tokens like in [`to_commit_iter()`][Object::try_to_commit_ref_iter()].
///
/// # Panic
///
diff --git a/vendor/gix/src/object/tag.rs b/vendor/gix/src/object/tag.rs
index ce9d7360a..77eaaa259 100644
--- a/vendor/gix/src/object/tag.rs
+++ b/vendor/gix/src/object/tag.rs
@@ -1,6 +1,17 @@
use crate::{ext::ObjectIdExt, Tag};
impl<'repo> Tag<'repo> {
+ /// Decode the entire tag object and return it for accessing all tag information.
+ ///
+ /// This never allocates.
+ ///
+ /// Note that the returned commit object does make lookup easy and should be
+ /// used for successive calls to string-ish information to avoid decoding the object
+ /// more than once.
+ pub fn decode(&self) -> Result<gix_object::TagRef<'_>, gix_object::decode::Error> {
+ gix_object::TagRef::from_bytes(&self.data)
+ }
+
/// Decode this tag partially and return the id of its target.
pub fn target_id(&self) -> Result<crate::Id<'repo>, gix_object::decode::Error> {
gix_object::TagRefIter::from_bytes(&self.data)
diff --git a/vendor/gix/src/object/tree/diff/mod.rs b/vendor/gix/src/object/tree/diff/mod.rs
index 5a3bf6ddf..447eeaa84 100644
--- a/vendor/gix/src/object/tree/diff/mod.rs
+++ b/vendor/gix/src/object/tree/diff/mod.rs
@@ -3,20 +3,15 @@ use gix_diff::tree::recorder::Location;
use crate::{bstr::BStr, Tree};
/// Returned by the `for_each` function to control flow.
-#[derive(Clone, Copy, PartialOrd, PartialEq, Ord, Eq, Hash)]
+#[derive(Default, Clone, Copy, PartialOrd, PartialEq, Ord, Eq, Hash)]
pub enum Action {
/// Continue the traversal of changes.
+ #[default]
Continue,
/// Stop the traversal of changes and stop calling this function.
Cancel,
}
-impl Default for Action {
- fn default() -> Self {
- Action::Continue
- }
-}
-
/// Represents any possible change in order to turn one tree into another.
#[derive(Debug, Clone, Copy)]
pub struct Change<'a, 'old, 'new> {
diff --git a/vendor/gix/src/object/tree/mod.rs b/vendor/gix/src/object/tree/mod.rs
index db094bcb9..bbd392289 100644
--- a/vendor/gix/src/object/tree/mod.rs
+++ b/vendor/gix/src/object/tree/mod.rs
@@ -22,6 +22,11 @@ impl<'repo> Tree<'repo> {
Id::from_id(self.id, self.repo)
}
+ /// Parse our tree data and return the parse tree for direct access to its entries.
+ pub fn decode(&self) -> Result<gix_object::TreeRef<'_>, gix_object::decode::Error> {
+ gix_object::TreeRef::from_bytes(&self.data)
+ }
+
// TODO: tests.
/// Follow a sequence of `path` components starting from this instance, and look them up one by one until the last component
/// is looked up and its tree entry is returned.
@@ -156,3 +161,15 @@ mod entry {
}
}
}
+
+mod _impls {
+ use crate::Tree;
+
+ impl TryFrom<Tree<'_>> for gix_object::Tree {
+ type Error = gix_object::decode::Error;
+
+ fn try_from(t: Tree<'_>) -> Result<Self, Self::Error> {
+ t.decode().map(Into::into)
+ }
+ }
+}
diff --git a/vendor/gix/src/open/mod.rs b/vendor/gix/src/open/mod.rs
index 77018f5a2..03c976204 100644
--- a/vendor/gix/src/open/mod.rs
+++ b/vendor/gix/src/open/mod.rs
@@ -1,6 +1,17 @@
use std::path::PathBuf;
-use crate::{bstr::BString, config, permission, Permissions};
+use crate::{bstr::BString, config};
+
+/// Permissions associated with various resources of a git repository
+#[derive(Debug, Clone)]
+pub struct Permissions {
+ /// Control which environment variables may be accessed.
+ pub env: permissions::Environment,
+ /// Permissions related where git configuration should be loaded from.
+ pub config: permissions::Config,
+ /// Permissions related to where `gitattributes` should be loaded from.
+ pub attributes: permissions::Attributes,
+}
/// The options used in [`ThreadSafeRepository::open_opts()`][crate::ThreadSafeRepository::open_opts()].
///
@@ -16,7 +27,7 @@ pub struct Options {
/// Define what is allowed while opening a repository.
pub permissions: Permissions,
pub(crate) git_dir_trust: Option<gix_sec::Trust>,
- /// Warning: this one is copied to to config::Cache - don't change it after repo open or keep in sync.
+ /// Warning: this one is copied to config::Cache - don't change it after repo open or keep in sync.
pub(crate) filter_config_section: Option<fn(&gix_config::file::Metadata) -> bool>,
pub(crate) lossy_config: Option<bool>,
pub(crate) lenient_config: bool,
@@ -44,11 +55,11 @@ pub enum Error {
#[error("The git directory at '{}' is considered unsafe as it's not owned by the current user.", .path.display())]
UnsafeGitDir { path: PathBuf },
#[error(transparent)]
- EnvironmentAccessDenied(#[from] permission::env_var::resource::Error),
+ EnvironmentAccessDenied(#[from] gix_sec::permission::Error<std::path::PathBuf>),
}
mod options;
-
+pub mod permissions;
mod repository;
#[cfg(test)]
diff --git a/vendor/gix/src/open/options.rs b/vendor/gix/src/open/options.rs
index fb648e3c2..b098d55c1 100644
--- a/vendor/gix/src/open/options.rs
+++ b/vendor/gix/src/open/options.rs
@@ -1,7 +1,7 @@
use std::path::PathBuf;
use super::{Error, Options};
-use crate::{bstr::BString, config, Permissions, ThreadSafeRepository};
+use crate::{bstr::BString, config, open::Permissions, ThreadSafeRepository};
impl Default for Options {
fn default() -> Self {
@@ -134,6 +134,9 @@ impl Options {
///
/// This is recommended for all applications that prefer correctness over usability.
/// `git` itself defaults to strict configuration mode, flagging incorrect configuration immediately.
+ ///
+ /// Failure to read configuration files due to IO errors will also be a hard error if this mode is enabled, otherwise
+ /// these errors will merely be logged.
pub fn strict_config(mut self, toggle: bool) -> Self {
self.lenient_config = !toggle;
self
diff --git a/vendor/gix/src/open/permissions.rs b/vendor/gix/src/open/permissions.rs
new file mode 100644
index 000000000..633575a9d
--- /dev/null
+++ b/vendor/gix/src/open/permissions.rs
@@ -0,0 +1,215 @@
+//! Various permissions to define what can be done when operating a [`Repository`][crate::Repository].
+use crate::open::Permissions;
+use gix_sec::Trust;
+
+/// Configure from which sources git configuration may be loaded.
+///
+/// Note that configuration from inside of the repository is always loaded as it's definitely required for correctness.
+#[derive(Copy, Clone, Ord, PartialOrd, PartialEq, Eq, Debug, Hash)]
+pub struct Config {
+ /// The git binary may come with configuration as part of its configuration, and if this is true (default false)
+ /// we will load the configuration of the git binary, if present and not a duplicate of the ones below.
+ ///
+ /// It's disabled by default as it may involve executing the git binary once per execution of the application.
+ pub git_binary: bool,
+ /// Whether to use the system configuration.
+ /// This is defined as `$(prefix)/etc/gitconfig` on unix.
+ pub system: bool,
+ /// Whether to use the git application configuration.
+ ///
+ /// A platform defined location for where a user's git application configuration should be located.
+ /// If `$XDG_CONFIG_HOME` is not set or empty, `$HOME/.config/git/config` will be used
+ /// on unix.
+ pub git: bool,
+ /// Whether to use the user configuration.
+ /// This is usually `~/.gitconfig` on unix.
+ pub user: bool,
+ /// Whether to use the configuration from environment variables.
+ pub env: bool,
+ /// Whether to follow include files are encountered in loaded configuration,
+ /// via `include` and `includeIf` sections.
+ pub includes: bool,
+}
+
+impl Config {
+ /// Allow everything which usually relates to a fully trusted environment
+ pub fn all() -> Self {
+ Config {
+ git_binary: false,
+ system: true,
+ git: true,
+ user: true,
+ env: true,
+ includes: true,
+ }
+ }
+
+ /// Load only configuration local to the git repository.
+ pub fn isolated() -> Self {
+ Config {
+ git_binary: false,
+ system: false,
+ git: false,
+ user: false,
+ env: false,
+ includes: false,
+ }
+ }
+}
+
+impl Default for Config {
+ fn default() -> Self {
+ Self::all()
+ }
+}
+
+/// Configure from which `gitattribute` files may be loaded.
+///
+/// Note that `.gitattribute` files from within the repository are always loaded.
+#[derive(Copy, Clone, Ord, PartialOrd, PartialEq, Eq, Debug, Hash)]
+pub struct Attributes {
+ /// The git binary may come with attribute configuration in its installation directory, and if this is true (default false)
+ /// we will load the configuration of the git binary.
+ ///
+ /// It's disabled by default as it involves executing the git binary once per execution of the application.
+ pub git_binary: bool,
+ /// Whether to use the system configuration.
+ /// This is typically defined as `$(prefix)/etc/gitconfig`.
+ pub system: bool,
+ /// Whether to use the git application configuration.
+ ///
+ /// A platform defined location for where a user's git application configuration should be located.
+ /// If `$XDG_CONFIG_HOME` is not set or empty, `$HOME/.config/git/attributes` will be used
+ /// on unix.
+ pub git: bool,
+}
+
+impl Attributes {
+ /// Allow everything which usually relates to a fully trusted environment
+ pub fn all() -> Self {
+ Attributes {
+ git_binary: false,
+ system: true,
+ git: true,
+ }
+ }
+
+ /// Allow loading attributes that are local to the git repository.
+ pub fn isolated() -> Self {
+ Attributes {
+ git_binary: false,
+ system: false,
+ git: false,
+ }
+ }
+}
+
+impl Default for Attributes {
+ fn default() -> Self {
+ Self::all()
+ }
+}
+
+/// Permissions related to the usage of environment variables
+#[derive(Debug, Clone, Copy)]
+pub struct Environment {
+ /// Control whether resources pointed to by `XDG_CONFIG_HOME` can be used when looking up common configuration values.
+ ///
+ /// Note that [`gix_sec::Permission::Forbid`] will cause the operation to abort if a resource is set via the XDG config environment.
+ pub xdg_config_home: gix_sec::Permission,
+ /// Control the way resources pointed to by the home directory (similar to `xdg_config_home`) may be used.
+ pub home: gix_sec::Permission,
+ /// Control if environment variables to configure the HTTP transport, like `http_proxy` may be used.
+ ///
+ /// Note that http-transport related environment variables prefixed with `GIT_` may also be included here
+ /// if they match this category like `GIT_HTTP_USER_AGENT`.
+ pub http_transport: gix_sec::Permission,
+ /// Control if the `EMAIL` environment variables may be read.
+ ///
+ /// Note that identity related environment variables prefixed with `GIT_` may also be included here
+ /// if they match this category.
+ pub identity: gix_sec::Permission,
+ /// Control if environment variables related to the object database are handled. This includes features and performance
+ /// options alike.
+ pub objects: gix_sec::Permission,
+ /// Control if resources pointed to by `GIT_*` prefixed environment variables can be used, **but only** if they
+ /// are not contained in any other category. This is a catch-all section.
+ pub git_prefix: gix_sec::Permission,
+ /// Control if resources pointed to by `SSH_*` prefixed environment variables can be used (like `SSH_ASKPASS`)
+ pub ssh_prefix: gix_sec::Permission,
+}
+
+impl Environment {
+ /// Allow access to the entire environment.
+ pub fn all() -> Self {
+ let allow = gix_sec::Permission::Allow;
+ Environment {
+ xdg_config_home: allow,
+ home: allow,
+ git_prefix: allow,
+ ssh_prefix: allow,
+ http_transport: allow,
+ identity: allow,
+ objects: allow,
+ }
+ }
+
+ /// Don't allow loading any environment variables.
+ pub fn isolated() -> Self {
+ let deny = gix_sec::Permission::Deny;
+ Environment {
+ xdg_config_home: deny,
+ home: deny,
+ ssh_prefix: deny,
+ git_prefix: deny,
+ http_transport: deny,
+ identity: deny,
+ objects: deny,
+ }
+ }
+}
+
+impl Permissions {
+ /// Secure permissions are similar to `all()`
+ pub fn secure() -> Self {
+ Permissions {
+ env: Environment::all(),
+ config: Config::all(),
+ attributes: Attributes::all(),
+ }
+ }
+
+ /// Everything is allowed with this set of permissions, thus we read all configuration and do what git typically
+ /// does with owned repositories.
+ pub fn all() -> Self {
+ Permissions {
+ env: Environment::all(),
+ config: Config::all(),
+ attributes: Attributes::all(),
+ }
+ }
+
+ /// Don't read any but the local git configuration and deny reading any environment variables.
+ pub fn isolated() -> Self {
+ Permissions {
+ config: Config::isolated(),
+ attributes: Attributes::isolated(),
+ env: Environment::isolated(),
+ }
+ }
+}
+
+impl gix_sec::trust::DefaultForLevel for Permissions {
+ fn default_for_level(level: Trust) -> Self {
+ match level {
+ Trust::Full => Permissions::all(),
+ Trust::Reduced => Permissions::secure(),
+ }
+ }
+}
+
+impl Default for Permissions {
+ fn default() -> Self {
+ Permissions::secure()
+ }
+}
diff --git a/vendor/gix/src/open/repository.rs b/vendor/gix/src/open/repository.rs
index 85dd91da7..c7702b5f6 100644
--- a/vendor/gix/src/open/repository.rs
+++ b/vendor/gix/src/open/repository.rs
@@ -10,7 +10,8 @@ use crate::{
cache::{interpolate_context, util::ApplyLeniency},
tree::{gitoxide, Core, Key, Safe},
},
- permission, Permissions, ThreadSafeRepository,
+ open::Permissions,
+ ThreadSafeRepository,
};
#[derive(Default, Clone)]
@@ -26,7 +27,7 @@ pub(crate) struct EnvironmentOverrides {
}
impl EnvironmentOverrides {
- fn from_env() -> Result<Self, permission::env_var::resource::Error> {
+ fn from_env() -> Result<Self, gix_sec::permission::Error<std::path::PathBuf>> {
let mut worktree_dir = None;
if let Some(path) = std::env::var_os(Core::WORKTREE.the_environment_override()) {
worktree_dir = PathBuf::from(path).into();
@@ -146,13 +147,18 @@ impl ThreadSafeRepository {
lenient_config,
bail_if_untrusted,
open_path_as_is: _,
- permissions: Permissions { ref env, config },
+ permissions:
+ Permissions {
+ ref env,
+ config,
+ attributes,
+ },
ref api_config_overrides,
ref cli_config_overrides,
ref current_dir,
} = options;
let current_dir = current_dir.as_deref().expect("BUG: current_dir must be set by caller");
- let git_dir_trust = git_dir_trust.expect("trust must be been determined by now");
+ let git_dir_trust = git_dir_trust.expect("trust must be determined by now");
// TODO: assure we handle the worktree-dir properly as we can have config per worktree with an extension.
// This would be something read in later as have to first check for extensions. Also this means
@@ -180,9 +186,7 @@ impl ThreadSafeRepository {
};
let head = refs.find("HEAD").ok();
let git_install_dir = crate::path::install_dir().ok();
- let home = std::env::var_os("HOME")
- .map(PathBuf::from)
- .and_then(|home| env.home.check_opt(home));
+ let home = gix_path::env::home_dir().and_then(|home| env.home.check_opt(home));
let mut filter_config_section = filter_config_section.unwrap_or(config::section::is_trusted);
let config = config::Cache::from_stage_one(
@@ -192,7 +196,8 @@ impl ThreadSafeRepository {
filter_config_section,
git_install_dir.as_deref(),
home.as_deref(),
- env.clone(),
+ *env,
+ attributes,
config,
lenient_config,
api_config_overrides,
@@ -266,7 +271,8 @@ impl ThreadSafeRepository {
config,
// used when spawning new repositories off this one when following worktrees
linked_worktree_options: options,
- index: gix_features::fs::MutableSnapshot::new().into(),
+ index: gix_fs::SharedFileSnapshotMut::new().into(),
+ shallow_commits: gix_fs::SharedFileSnapshotMut::new().into(),
})
}
}
diff --git a/vendor/gix/src/prelude.rs b/vendor/gix/src/prelude.rs
new file mode 100644
index 000000000..36fbfc7b1
--- /dev/null
+++ b/vendor/gix/src/prelude.rs
@@ -0,0 +1,4 @@
+pub use gix_features::parallel::reduce::Finalize;
+pub use gix_odb::{Find, FindExt, Header, HeaderExt, Write};
+
+pub use crate::ext::*;
diff --git a/vendor/gix/src/progress.rs b/vendor/gix/src/progress.rs
new file mode 100644
index 000000000..0a88aa044
--- /dev/null
+++ b/vendor/gix/src/progress.rs
@@ -0,0 +1,3 @@
+#[cfg(feature = "progress-tree")]
+pub use gix_features::progress::prodash::tree;
+pub use gix_features::progress::*;
diff --git a/vendor/gix/src/remote/connect.rs b/vendor/gix/src/remote/connect.rs
index 8e656975e..3dbd93486 100644
--- a/vendor/gix/src/remote/connect.rs
+++ b/vendor/gix/src/remote/connect.rs
@@ -1,7 +1,7 @@
#![allow(clippy::result_large_err)]
use gix_protocol::transport::client::Transport;
-use crate::{remote::Connection, Progress, Remote};
+use crate::{remote::Connection, Remote};
mod error {
use crate::{bstr::BString, config, remote};
@@ -51,17 +51,15 @@ impl<'repo> Remote<'repo> {
///
/// Note that this method expects the `transport` to be created by the user, which would involve the [`url()`][Self::url()].
/// It's meant to be used when async operation is needed with runtimes of the user's choice.
- pub fn to_connection_with_transport<T, P>(&self, transport: T, progress: P) -> Connection<'_, 'repo, T, P>
+ pub fn to_connection_with_transport<T>(&self, transport: T) -> Connection<'_, 'repo, T>
where
T: Transport,
- P: Progress,
{
Connection {
remote: self,
authenticate: None,
transport_options: None,
transport,
- progress,
}
}
@@ -75,14 +73,10 @@ impl<'repo> Remote<'repo> {
/// [to_connection_with_transport()][Self::to_connection_with_transport()].
#[cfg(any(feature = "blocking-network-client", feature = "async-network-client-async-std"))]
#[gix_protocol::maybe_async::maybe_async]
- pub async fn connect<P>(
+ pub async fn connect(
&self,
direction: crate::remote::Direction,
- progress: P,
- ) -> Result<Connection<'_, 'repo, Box<dyn Transport + Send>, P>, Error>
- where
- P: Progress,
- {
+ ) -> Result<Connection<'_, 'repo, Box<dyn Transport + Send>>, Error> {
let (url, version) = self.sanitized_url_and_version(direction)?;
#[cfg(feature = "blocking-network-client")]
let scheme_is_ssh = url.scheme == gix_url::Scheme::Ssh;
@@ -98,7 +92,7 @@ impl<'repo> Remote<'repo> {
},
)
.await?;
- Ok(self.to_connection_with_transport(transport, progress))
+ Ok(self.to_connection_with_transport(transport))
}
/// Produce the sanitized URL and protocol version to use as obtained by querying the repository configuration.
diff --git a/vendor/gix/src/remote/connection/access.rs b/vendor/gix/src/remote/connection/access.rs
index e4c31c3f5..eba603da0 100644
--- a/vendor/gix/src/remote/connection/access.rs
+++ b/vendor/gix/src/remote/connection/access.rs
@@ -4,7 +4,7 @@ use crate::{
};
/// Builder
-impl<'a, 'repo, T, P> Connection<'a, 'repo, T, P> {
+impl<'a, 'repo, T> Connection<'a, 'repo, T> {
/// Set a custom credentials callback to provide credentials if the remotes require authentication.
///
/// Otherwise we will use the git configuration to perform the same task as the `git credential` helper program,
@@ -37,8 +37,26 @@ impl<'a, 'repo, T, P> Connection<'a, 'repo, T, P> {
}
}
+/// Mutation
+impl<'a, 'repo, T> Connection<'a, 'repo, T> {
+ /// Like [`with_credentials()`][Self::with_credentials()], but without consuming the connection.
+ pub fn set_credentials(
+ &mut self,
+ helper: impl FnMut(gix_credentials::helper::Action) -> gix_credentials::protocol::Result + 'a,
+ ) -> &mut Self {
+ self.authenticate = Some(Box::new(helper));
+ self
+ }
+
+ /// Like [`with_transport_options()`][Self::with_transport_options()], but without consuming the connection.
+ pub fn set_transport_options(&mut self, config: Box<dyn std::any::Any>) -> &mut Self {
+ self.transport_options = Some(config);
+ self
+ }
+}
+
/// Access
-impl<'a, 'repo, T, P> Connection<'a, 'repo, T, P> {
+impl<'a, 'repo, T> Connection<'a, 'repo, T> {
/// A utility to return a function that will use this repository's configuration to obtain credentials, similar to
/// what `git credential` is doing.
///
diff --git a/vendor/gix/src/remote/connection/fetch/error.rs b/vendor/gix/src/remote/connection/fetch/error.rs
index 0e6a4b840..afcacca13 100644
--- a/vendor/gix/src/remote/connection/fetch/error.rs
+++ b/vendor/gix/src/remote/connection/fetch/error.rs
@@ -28,6 +28,21 @@ pub enum Error {
path: std::path::PathBuf,
source: std::io::Error,
},
+ #[error(transparent)]
+ ShallowOpen(#[from] crate::shallow::open::Error),
+ #[error("Server lack feature {feature:?}: {description}")]
+ MissingServerFeature {
+ feature: &'static str,
+ description: &'static str,
+ },
+ #[error("Could not write 'shallow' file to incorporate remote updates after fetching")]
+ WriteShallowFile(#[from] crate::shallow::write::Error),
+ #[error("'shallow' file could not be locked in preparation for writing changes")]
+ LockShallowFile(#[from] gix_lock::acquire::Error),
+ #[error("Could not obtain configuration to learn if shallow remotes should be rejected")]
+ RejectShallowRemoteConfig(#[from] config::boolean::Error),
+ #[error("Receiving objects from shallow remotes is prohibited due to the value of `clone.rejectShallow`")]
+ RejectShallowRemote,
}
impl gix_protocol::transport::IsSpuriousError for Error {
diff --git a/vendor/gix/src/remote/connection/fetch/mod.rs b/vendor/gix/src/remote/connection/fetch/mod.rs
index 4ce631b1e..a51ae7c54 100644
--- a/vendor/gix/src/remote/connection/fetch/mod.rs
+++ b/vendor/gix/src/remote/connection/fetch/mod.rs
@@ -116,10 +116,9 @@ pub mod prepare {
}
}
-impl<'remote, 'repo, T, P> Connection<'remote, 'repo, T, P>
+impl<'remote, 'repo, T> Connection<'remote, 'repo, T>
where
T: Transport,
- P: Progress,
{
/// Perform a handshake with the remote and obtain a ref-map with `options`, and from there one
/// Note that at this point, the `transport` should already be configured using the [`transport_mut()`][Self::transport_mut()]
@@ -137,23 +136,25 @@ where
#[gix_protocol::maybe_async::maybe_async]
pub async fn prepare_fetch(
mut self,
+ progress: impl Progress,
options: ref_map::Options,
- ) -> Result<Prepare<'remote, 'repo, T, P>, prepare::Error> {
+ ) -> Result<Prepare<'remote, 'repo, T>, prepare::Error> {
if self.remote.refspecs(remote::Direction::Fetch).is_empty() {
return Err(prepare::Error::MissingRefSpecs);
}
- let ref_map = self.ref_map_inner(options).await?;
+ let ref_map = self.ref_map_inner(progress, options).await?;
Ok(Prepare {
con: Some(self),
ref_map,
dry_run: DryRun::No,
reflog_message: None,
write_packed_refs: WritePackedRefs::Never,
+ shallow: Default::default(),
})
}
}
-impl<'remote, 'repo, T, P> Prepare<'remote, 'repo, T, P>
+impl<'remote, 'repo, T> Prepare<'remote, 'repo, T>
where
T: Transport,
{
@@ -170,19 +171,20 @@ mod receive_pack;
pub mod refs;
/// A structure to hold the result of the handshake with the remote and configure the upcoming fetch operation.
-pub struct Prepare<'remote, 'repo, T, P>
+pub struct Prepare<'remote, 'repo, T>
where
T: Transport,
{
- con: Option<Connection<'remote, 'repo, T, P>>,
+ con: Option<Connection<'remote, 'repo, T>>,
ref_map: RefMap,
dry_run: DryRun,
reflog_message: Option<RefLogMessage>,
write_packed_refs: WritePackedRefs,
+ shallow: remote::fetch::Shallow,
}
/// Builder
-impl<'remote, 'repo, T, P> Prepare<'remote, 'repo, T, P>
+impl<'remote, 'repo, T> Prepare<'remote, 'repo, T>
where
T: Transport,
{
@@ -212,9 +214,17 @@ where
self.reflog_message = reflog_message.into();
self
}
+
+ /// Define what to do when the current repository is a shallow clone.
+ ///
+ /// *Has no effect if the current repository is not as shallow clone.*
+ pub fn with_shallow(mut self, shallow: remote::fetch::Shallow) -> Self {
+ self.shallow = shallow;
+ self
+ }
}
-impl<'remote, 'repo, T, P> Drop for Prepare<'remote, 'repo, T, P>
+impl<'remote, 'repo, T> Drop for Prepare<'remote, 'repo, T>
where
T: Transport,
{
diff --git a/vendor/gix/src/remote/connection/fetch/negotiate.rs b/vendor/gix/src/remote/connection/fetch/negotiate.rs
index f5051ec72..771c5acba 100644
--- a/vendor/gix/src/remote/connection/fetch/negotiate.rs
+++ b/vendor/gix/src/remote/connection/fetch/negotiate.rs
@@ -1,3 +1,5 @@
+use crate::remote::fetch;
+
/// The way the negotiation is performed
#[derive(Copy, Clone)]
pub(crate) enum Algorithm {
@@ -16,6 +18,7 @@ pub enum Error {
/// Negotiate one round with `algo` by looking at `ref_map` and adjust `arguments` to contain the haves and wants.
/// If this is not the first round, the `previous_response` is set with the last recorded server response.
/// Returns `true` if the negotiation is done from our side so the server won't keep asking.
+#[allow(clippy::too_many_arguments)]
pub(crate) fn one_round(
algo: Algorithm,
round: usize,
@@ -24,13 +27,20 @@ pub(crate) fn one_round(
fetch_tags: crate::remote::fetch::Tags,
arguments: &mut gix_protocol::fetch::Arguments,
_previous_response: Option<&gix_protocol::fetch::Response>,
+ shallow: Option<&fetch::Shallow>,
) -> Result<bool, Error> {
let tag_refspec_to_ignore = fetch_tags
.to_refspec()
.filter(|_| matches!(fetch_tags, crate::remote::fetch::Tags::Included));
+ if let Some(fetch::Shallow::Deepen(0)) = shallow {
+ // Avoid deepening (relative) with zero as it seems to upset the server. Git also doesn't actually
+ // perform the negotiation for some reason (couldn't find it in code).
+ return Ok(true);
+ }
+
match algo {
Algorithm::Naive => {
- assert_eq!(round, 1, "Naive always finishes after the first round, and claims.");
+ assert_eq!(round, 1, "Naive always finishes after the first round, it claims.");
let mut has_missing_tracking_branch = false;
for mapping in &ref_map.mappings {
if tag_refspec_to_ignore.map_or(false, |tag_spec| {
@@ -65,10 +75,11 @@ pub(crate) fn one_round(
}
}
- if has_missing_tracking_branch {
+ if has_missing_tracking_branch || (shallow.is_some() && arguments.is_empty()) {
if let Ok(Some(r)) = repo.head_ref() {
if let Some(id) = r.target().try_id() {
arguments.have(id);
+ arguments.want(id);
}
}
}
diff --git a/vendor/gix/src/remote/connection/fetch/receive_pack.rs b/vendor/gix/src/remote/connection/fetch/receive_pack.rs
index 686de5999..99560fbca 100644
--- a/vendor/gix/src/remote/connection/fetch/receive_pack.rs
+++ b/vendor/gix/src/remote/connection/fetch/receive_pack.rs
@@ -1,23 +1,25 @@
-use std::sync::atomic::AtomicBool;
+use std::sync::atomic::{AtomicBool, Ordering};
use gix_odb::FindExt;
-use gix_protocol::transport::client::Transport;
+use gix_protocol::{
+ fetch::Arguments,
+ transport::{client::Transport, packetline::read::ProgressAction},
+};
use crate::{
+ config::tree::Clone,
remote,
remote::{
connection::fetch::config,
fetch,
- fetch::{negotiate, refs, Error, Outcome, Prepare, ProgressId, RefLogMessage, Status},
+ fetch::{negotiate, refs, Error, Outcome, Prepare, ProgressId, RefLogMessage, Shallow, Status},
},
- Progress,
+ Progress, Repository,
};
-impl<'remote, 'repo, T, P> Prepare<'remote, 'repo, T, P>
+impl<'remote, 'repo, T> Prepare<'remote, 'repo, T>
where
T: Transport,
- P: Progress,
- P::SubProgress: 'static,
{
/// Receive the pack and perform the operation as configured by git via `gix-config` or overridden by various builder methods.
/// Return `Ok(None)` if there was nothing to do because all remote refs are at the same state as they are locally, or `Ok(Some(outcome))`
@@ -62,14 +64,18 @@ where
/// - `gitoxide.userAgent` is read to obtain the application user agent for git servers and for HTTP servers as well.
///
#[gix_protocol::maybe_async::maybe_async]
- pub async fn receive(mut self, should_interrupt: &AtomicBool) -> Result<Outcome, Error> {
+ pub async fn receive<P>(mut self, mut progress: P, should_interrupt: &AtomicBool) -> Result<Outcome, Error>
+ where
+ P: Progress,
+ P::SubProgress: 'static,
+ {
let mut con = self.con.take().expect("receive() can only be called once");
let handshake = &self.ref_map.handshake;
let protocol_version = handshake.server_protocol_version;
let fetch = gix_protocol::Command::Fetch;
- let progress = &mut con.progress;
+ let progress = &mut progress;
let repo = con.remote.repo;
let fetch_features = {
let mut f = fetch.default_features(protocol_version, &handshake.capabilities);
@@ -82,10 +88,17 @@ where
let mut arguments = gix_protocol::fetch::Arguments::new(protocol_version, fetch_features);
if matches!(con.remote.fetch_tags, crate::remote::fetch::Tags::Included) {
if !arguments.can_use_include_tag() {
- unimplemented!("we expect servers to support 'include-tag', otherwise we have to implement another pass to fetch attached tags separately");
+ return Err(Error::MissingServerFeature {
+ feature: "include-tag",
+ description:
+ // NOTE: if this is an issue, we could probably do what's proposed here.
+ "To make this work we would have to implement another pass to fetch attached tags separately",
+ });
}
arguments.use_include_tag();
}
+ let (shallow_commits, mut shallow_lock) = add_shallow_args(&mut arguments, &self.shallow, repo)?;
+
let mut previous_response = None::<gix_protocol::fetch::Response>;
let mut round = 1;
@@ -108,6 +121,7 @@ where
con.remote.fetch_tags,
&mut arguments,
previous_response.as_ref(),
+ (self.shallow != Shallow::NoChange).then_some(&self.shallow),
) {
Ok(_) if arguments.is_empty() => {
gix_protocol::indicate_end_of_interaction(&mut con.transport).await.ok();
@@ -137,20 +151,35 @@ where
round += 1;
let mut reader = arguments.send(&mut con.transport, is_done).await?;
if sideband_all {
- setup_remote_progress(progress, &mut reader);
+ setup_remote_progress(progress, &mut reader, should_interrupt);
}
let response = gix_protocol::fetch::Response::from_line_reader(protocol_version, &mut reader).await?;
if response.has_pack() {
progress.step();
progress.set_name("receiving pack");
if !sideband_all {
- setup_remote_progress(progress, &mut reader);
+ setup_remote_progress(progress, &mut reader, should_interrupt);
}
+ previous_response = Some(response);
break 'negotiation reader;
} else {
previous_response = Some(response);
}
};
+ let previous_response = previous_response.expect("knowledge of a pack means a response was received");
+ if !previous_response.shallow_updates().is_empty() && shallow_lock.is_none() {
+ let reject_shallow_remote = repo
+ .config
+ .resolved
+ .boolean_filter_by_key("clone.rejectShallow", &mut repo.filter_config_section())
+ .map(|val| Clone::REJECT_SHALLOW.enrich_error(val))
+ .transpose()?
+ .unwrap_or(false);
+ if reject_shallow_remote {
+ return Err(Error::RejectShallowRemote);
+ }
+ shallow_lock = acquire_shallow_lock(repo).map(Some)?;
+ }
let options = gix_pack::bundle::write::Options {
thread_limit: config::index_threads(repo)?,
@@ -170,7 +199,7 @@ where
reader
},
Some(repo.objects.store_ref().path().join("pack")),
- con.progress,
+ progress,
should_interrupt,
Some(Box::new({
let repo = repo.clone();
@@ -187,6 +216,12 @@ where
gix_protocol::indicate_end_of_interaction(&mut con.transport).await.ok();
}
+ if let Some(shallow_lock) = shallow_lock {
+ if !previous_response.shallow_updates().is_empty() {
+ crate::shallow::write(shallow_lock, shallow_commits, previous_response.shallow_updates())?;
+ }
+ }
+
let update_refs = refs::update(
repo,
self.reflog_message
@@ -221,9 +256,61 @@ where
}
}
+fn acquire_shallow_lock(repo: &Repository) -> Result<gix_lock::File, Error> {
+ gix_lock::File::acquire_to_update_resource(repo.shallow_file(), gix_lock::acquire::Fail::Immediately, None)
+ .map_err(Into::into)
+}
+
+fn add_shallow_args(
+ args: &mut Arguments,
+ shallow: &Shallow,
+ repo: &Repository,
+) -> Result<(Option<crate::shallow::Commits>, Option<gix_lock::File>), Error> {
+ let expect_change = *shallow != Shallow::NoChange;
+ let shallow_lock = expect_change.then(|| acquire_shallow_lock(repo)).transpose()?;
+
+ let shallow_commits = repo.shallow_commits()?;
+ if (shallow_commits.is_some() || expect_change) && !args.can_use_shallow() {
+ // NOTE: if this is an issue, we can always unshallow the repo ourselves.
+ return Err(Error::MissingServerFeature {
+ feature: "shallow",
+ description: "shallow clones need server support to remain shallow, otherwise bigger than expected packs are sent effectively unshallowing the repository",
+ });
+ }
+ if let Some(shallow_commits) = &shallow_commits {
+ for commit in shallow_commits.iter() {
+ args.shallow(commit);
+ }
+ }
+ match shallow {
+ Shallow::NoChange => {}
+ Shallow::DepthAtRemote(commits) => args.deepen(commits.get() as usize),
+ Shallow::Deepen(commits) => {
+ args.deepen(*commits as usize);
+ args.deepen_relative();
+ }
+ Shallow::Since { cutoff } => {
+ args.deepen_since(cutoff.seconds_since_unix_epoch as usize);
+ }
+ Shallow::Exclude {
+ remote_refs,
+ since_cutoff,
+ } => {
+ if let Some(cutoff) = since_cutoff {
+ args.deepen_since(cutoff.seconds_since_unix_epoch as usize);
+ }
+ for ref_ in remote_refs {
+ args.deepen_not(ref_.as_ref().as_bstr());
+ }
+ }
+ }
+ Ok((shallow_commits, shallow_lock))
+}
+
fn setup_remote_progress<P>(
progress: &mut P,
reader: &mut Box<dyn gix_protocol::transport::client::ExtendedBufRead + Unpin + '_>,
+ should_interrupt: &AtomicBool,
) where
P: Progress,
P::SubProgress: 'static,
@@ -231,8 +318,23 @@ fn setup_remote_progress<P>(
use gix_protocol::transport::client::ExtendedBufRead;
reader.set_progress_handler(Some(Box::new({
let mut remote_progress = progress.add_child_with_id("remote", ProgressId::RemoteProgress.into());
+ // SAFETY: Ugh, so, with current Rust I can't declare lifetimes in the involved traits the way they need to
+ // be and I also can't use scoped threads to pump from local scopes to an Arc version that could be
+ // used here due to the this being called from sync AND async code (and the async version doesn't work
+ // with a surrounding `std::thread::scope()`.
+ // Thus there is only claiming this is 'static which we know works for *our* implementations of ExtendedBufRead
+ // and typical implementations, but of course it's possible for user code to come along and actually move this
+ // handler into a context where it can outlive the current function. Is this going to happen? Probably not unless
+ // somebody really wants to break it. So, with standard usage this value is never used past its actual lifetime.
+ #[allow(unsafe_code)]
+ let should_interrupt: &'static AtomicBool = unsafe { std::mem::transmute(should_interrupt) };
move |is_err: bool, data: &[u8]| {
- gix_protocol::RemoteProgress::translate_to_progress(is_err, data, &mut remote_progress)
+ gix_protocol::RemoteProgress::translate_to_progress(is_err, data, &mut remote_progress);
+ if should_interrupt.load(Ordering::Relaxed) {
+ ProgressAction::Interrupt
+ } else {
+ ProgressAction::Continue
+ }
}
}) as gix_protocol::transport::client::HandleProgress));
}
diff --git a/vendor/gix/src/remote/connection/mod.rs b/vendor/gix/src/remote/connection/mod.rs
index 09943ecc4..02a09926a 100644
--- a/vendor/gix/src/remote/connection/mod.rs
+++ b/vendor/gix/src/remote/connection/mod.rs
@@ -12,12 +12,11 @@ pub type AuthenticateFn<'a> = Box<dyn FnMut(gix_credentials::helper::Action) ->
///
/// It can be used to perform a variety of operations with the remote without worrying about protocol details,
/// much like a remote procedure call.
-pub struct Connection<'a, 'repo, T, P> {
+pub struct Connection<'a, 'repo, T> {
pub(crate) remote: &'a Remote<'repo>,
pub(crate) authenticate: Option<AuthenticateFn<'a>>,
pub(crate) transport_options: Option<Box<dyn std::any::Any>>,
pub(crate) transport: T,
- pub(crate) progress: P,
}
mod access;
diff --git a/vendor/gix/src/remote/connection/ref_map.rs b/vendor/gix/src/remote/connection/ref_map.rs
index 0206e9002..abf9c8e00 100644
--- a/vendor/gix/src/remote/connection/ref_map.rs
+++ b/vendor/gix/src/remote/connection/ref_map.rs
@@ -71,10 +71,9 @@ impl Default for Options {
}
}
-impl<'remote, 'repo, T, P> Connection<'remote, 'repo, T, P>
+impl<'remote, 'repo, T> Connection<'remote, 'repo, T>
where
T: Transport,
- P: Progress,
{
/// List all references on the remote that have been filtered through our remote's [`refspecs`][crate::Remote::refspecs()]
/// for _fetching_.
@@ -94,8 +93,8 @@ where
/// - `gitoxide.userAgent` is read to obtain the application user agent for git servers and for HTTP servers as well.
#[allow(clippy::result_large_err)]
#[gix_protocol::maybe_async::maybe_async]
- pub async fn ref_map(mut self, options: Options) -> Result<fetch::RefMap, Error> {
- let res = self.ref_map_inner(options).await;
+ pub async fn ref_map(mut self, progress: impl Progress, options: Options) -> Result<fetch::RefMap, Error> {
+ let res = self.ref_map_inner(progress, options).await;
gix_protocol::indicate_end_of_interaction(&mut self.transport)
.await
.ok();
@@ -106,6 +105,7 @@ where
#[gix_protocol::maybe_async::maybe_async]
pub(crate) async fn ref_map_inner(
&mut self,
+ progress: impl Progress,
Options {
prefix_from_spec_as_filter_on_remote,
handshake_parameters,
@@ -125,7 +125,12 @@ where
s
};
let remote = self
- .fetch_refs(prefix_from_spec_as_filter_on_remote, handshake_parameters, &specs)
+ .fetch_refs(
+ prefix_from_spec_as_filter_on_remote,
+ handshake_parameters,
+ &specs,
+ progress,
+ )
.await?;
let num_explicit_specs = self.remote.fetch_specs.len();
let group = gix_refspec::MatchGroup::from_fetch_specs(specs.iter().map(|s| s.to_ref()));
@@ -179,6 +184,7 @@ where
filter_by_prefix: bool,
extra_parameters: Vec<(String, Option<String>)>,
refspecs: &[gix_refspec::RefSpec],
+ mut progress: impl Progress,
) -> Result<HandshakeWithRefs, Error> {
let mut credentials_storage;
let url = self.transport.to_url();
@@ -209,8 +215,7 @@ where
self.transport.configure(&**config)?;
}
let mut outcome =
- gix_protocol::fetch::handshake(&mut self.transport, authenticate, extra_parameters, &mut self.progress)
- .await?;
+ gix_protocol::fetch::handshake(&mut self.transport, authenticate, extra_parameters, &mut progress).await?;
let refs = match outcome.refs.take() {
Some(refs) => refs,
None => {
@@ -236,7 +241,7 @@ where
}
Ok(gix_protocol::ls_refs::Action::Continue)
},
- &mut self.progress,
+ &mut progress,
)
.await?
}
diff --git a/vendor/gix/src/remote/fetch.rs b/vendor/gix/src/remote/fetch.rs
index 4add96a65..0001447cb 100644
--- a/vendor/gix/src/remote/fetch.rs
+++ b/vendor/gix/src/remote/fetch.rs
@@ -19,24 +19,19 @@ pub(crate) enum WritePackedRefs {
}
/// Describe how to handle tags when fetching
-#[derive(Debug, Copy, Clone, PartialEq, Eq)]
+#[derive(Default, Debug, Copy, Clone, PartialEq, Eq)]
pub enum Tags {
/// Fetch all tags from the remote, even if these are not reachable from objects referred to by our refspecs.
All,
/// Fetch only the tags that point to the objects being sent.
/// That way, annotated tags that point to an object we receive are automatically transmitted and their refs are created.
/// The same goes for lightweight tags.
+ #[default]
Included,
/// Do not fetch any tags.
None,
}
-impl Default for Tags {
- fn default() -> Self {
- Tags::Included
- }
-}
-
impl Tags {
/// Obtain a refspec that determines whether or not to fetch all tags, depending on this variant.
///
@@ -52,6 +47,47 @@ impl Tags {
}
}
+/// Describe how shallow clones are handled when fetching, with variants defining how the *shallow boundary* is handled.
+///
+/// The *shallow boundary* is a set of commits whose parents are not present in the repository.
+#[derive(Default, Debug, Clone, PartialEq, Eq)]
+pub enum Shallow {
+ /// Fetch all changes from the remote without affecting the shallow boundary at all.
+ #[default]
+ NoChange,
+ /// Receive update to `depth` commits in the history of the refs to fetch (from the viewpoint of the remote),
+ /// with the value of `1` meaning to receive only the commit a ref is pointing to.
+ ///
+ /// This may update the shallow boundary to increase or decrease the amount of available history.
+ DepthAtRemote(std::num::NonZeroU32),
+ /// Increase the number of commits and thus expand the shallow boundary by `depth` commits as seen from our local
+ /// shallow boundary, with a value of `0` having no effect.
+ Deepen(u32),
+ /// Set the shallow boundary at the `cutoff` time, meaning that there will be no commits beyond that time.
+ Since {
+ /// The date beyond which there will be no history.
+ cutoff: gix_date::Time,
+ },
+ /// Receive all history excluding all commits reachable from `remote_refs`. These can be long or short
+ /// ref names or tag names.
+ Exclude {
+ /// The ref names to exclude, short or long. Note that ambiguous short names will cause the remote to abort
+ /// without an error message being transferred (because the protocol does not support it)
+ remote_refs: Vec<gix_ref::PartialName>,
+ /// If some, this field has the same meaning as [`Shallow::Since`] which can be used in combination
+ /// with excluded references.
+ since_cutoff: Option<gix_date::Time>,
+ },
+}
+
+impl Shallow {
+ /// Produce a variant that causes the repository to loose its shallow boundary, effectively by extending it
+ /// beyond all limits.
+ pub fn undo() -> Self {
+ Shallow::DepthAtRemote((i32::MAX as u32).try_into().expect("valid at compile time"))
+ }
+}
+
/// Information about the relationship between our refspecs, and remote references with their local counterparts.
#[derive(Default, Debug, Clone)]
#[cfg(any(feature = "blocking-network-client", feature = "async-network-client"))]
diff --git a/vendor/gix/src/remote/save.rs b/vendor/gix/src/remote/save.rs
index 0e347551e..ad6a75b14 100644
--- a/vendor/gix/src/remote/save.rs
+++ b/vendor/gix/src/remote/save.rs
@@ -32,6 +32,7 @@ impl Remote<'_> {
/// Note that all sections named `remote "<name>"` will be cleared of all values we are about to write,
/// and the last `remote "<name>"` section will be containing all relevant values so that reloading the remote
/// from `config` would yield the same in-memory state.
+ #[allow(clippy::result_large_err)]
pub fn save_to(&self, config: &mut gix_config::File<'static>) -> Result<(), Error> {
fn as_key(name: &str) -> gix_config::parse::section::Key<'_> {
name.try_into().expect("valid")
@@ -109,6 +110,7 @@ impl Remote<'_> {
/// Note that this sets a name for anonymous remotes, but overwrites the name for those who were named before.
/// If this name is different from the current one, the git configuration will still contain the previous name,
/// and the caller should account for that.
+ #[allow(clippy::result_large_err)]
pub fn save_as_to(
&mut self,
name: impl Into<BString>,
diff --git a/vendor/gix/src/repository/attributes.rs b/vendor/gix/src/repository/attributes.rs
new file mode 100644
index 000000000..252529761
--- /dev/null
+++ b/vendor/gix/src/repository/attributes.rs
@@ -0,0 +1,50 @@
+//! exclude information
+use crate::Repository;
+
+impl Repository {
+ /// Configure a file-system cache for accessing git attributes *and* excludes on a per-path basis.
+ ///
+ /// Use `attribute_source` to specify where to read attributes from. Also note that exclude information will
+ /// always try to read `.gitignore` files from disk before trying to read it from the `index`.
+ ///
+ /// Note that no worktree is required for this to work, even though access to in-tree `.gitattributes` and `.gitignore` files
+ /// would require a non-empty `index` that represents a git tree.
+ ///
+ /// This takes into consideration all the usual repository configuration, namely:
+ ///
+ /// * `$XDG_CONFIG_HOME/…/ignore|attributes` if `core.excludesFile|attributesFile` is *not* set, otherwise use the configured file.
+ /// * `$GIT_DIR/info/exclude|attributes` if present.
+ // TODO: test, provide higher-level custom Cache wrapper that is much easier to use and doesn't panic when accessing entries
+ // by non-relative path.
+ pub fn attributes(
+ &self,
+ index: &gix_index::State,
+ attributes_source: gix_worktree::cache::state::attributes::Source,
+ ignore_source: gix_worktree::cache::state::ignore::Source,
+ exclude_overrides: Option<gix_ignore::Search>,
+ ) -> Result<gix_worktree::Cache, crate::attributes::Error> {
+ let case = if self.config.ignore_case {
+ gix_glob::pattern::Case::Fold
+ } else {
+ gix_glob::pattern::Case::Sensitive
+ };
+ let (attributes, mut buf) = self.config.assemble_attribute_globals(
+ self.git_dir(),
+ attributes_source,
+ self.options.permissions.attributes,
+ )?;
+ let ignore =
+ self.config
+ .assemble_exclude_globals(self.git_dir(), exclude_overrides, ignore_source, &mut buf)?;
+ let state = gix_worktree::cache::State::AttributesAndIgnoreStack { attributes, ignore };
+ let attribute_list = state.id_mappings_from_index(index, index.path_backing(), ignore_source, case);
+ Ok(gix_worktree::Cache::new(
+ // this is alright as we don't cause mutation of that directory, it's virtual.
+ self.work_dir().unwrap_or(self.git_dir()),
+ state,
+ case,
+ buf,
+ attribute_list,
+ ))
+ }
+}
diff --git a/vendor/gix/src/repository/excludes.rs b/vendor/gix/src/repository/excludes.rs
new file mode 100644
index 000000000..6281549e0
--- /dev/null
+++ b/vendor/gix/src/repository/excludes.rs
@@ -0,0 +1,45 @@
+//! exclude information
+use crate::{config, Repository};
+impl Repository {
+ /// Configure a file-system cache checking if files below the repository are excluded, reading `.gitignore` files from
+ /// the specified `source`.
+ ///
+ /// Note that no worktree is required for this to work, even though access to in-tree `.gitignore` files would require
+ /// a non-empty `index` that represents a tree with `.gitignore` files.
+ ///
+ /// This takes into consideration all the usual repository configuration, namely:
+ ///
+ /// * `$XDG_CONFIG_HOME/…/ignore` if `core.excludesFile` is *not* set, otherwise use the configured file.
+ /// * `$GIT_DIR/info/exclude` if present.
+ ///
+ /// When only excludes are desired, this is the most efficient way to obtain them. Otherwise use
+ /// [`Repository::attributes()`] for accessing both attributes and excludes.
+ // TODO: test, provide higher-level custom Cache wrapper that is much easier to use and doesn't panic when accessing entries
+ // by non-relative path.
+ pub fn excludes(
+ &self,
+ index: &gix_index::State,
+ overrides: Option<gix_ignore::Search>,
+ source: gix_worktree::cache::state::ignore::Source,
+ ) -> Result<gix_worktree::Cache, config::exclude_stack::Error> {
+ let case = if self.config.ignore_case {
+ gix_glob::pattern::Case::Fold
+ } else {
+ gix_glob::pattern::Case::Sensitive
+ };
+ let mut buf = Vec::with_capacity(512);
+ let ignore = self
+ .config
+ .assemble_exclude_globals(self.git_dir(), overrides, source, &mut buf)?;
+ let state = gix_worktree::cache::State::IgnoreStack(ignore);
+ let attribute_list = state.id_mappings_from_index(index, index.path_backing(), source, case);
+ Ok(gix_worktree::Cache::new(
+ // this is alright as we don't cause mutation of that directory, it's virtual.
+ self.work_dir().unwrap_or(self.git_dir()),
+ state,
+ case,
+ buf,
+ attribute_list,
+ ))
+ }
+}
diff --git a/vendor/gix/src/repository/impls.rs b/vendor/gix/src/repository/impls.rs
index 6cf2b2e9b..5da55290c 100644
--- a/vendor/gix/src/repository/impls.rs
+++ b/vendor/gix/src/repository/impls.rs
@@ -8,6 +8,7 @@ impl Clone for crate::Repository {
self.config.clone(),
self.options.clone(),
self.index.clone(),
+ self.shallow_commits.clone(),
)
}
}
@@ -40,6 +41,7 @@ impl From<&crate::ThreadSafeRepository> for crate::Repository {
repo.config.clone(),
repo.linked_worktree_options.clone(),
repo.index.clone(),
+ repo.shallow_commits.clone(),
)
}
}
@@ -54,6 +56,7 @@ impl From<crate::ThreadSafeRepository> for crate::Repository {
repo.config,
repo.linked_worktree_options,
repo.index,
+ repo.shallow_commits,
)
}
}
@@ -68,6 +71,7 @@ impl From<crate::Repository> for crate::ThreadSafeRepository {
config: r.config,
linked_worktree_options: r.options,
index: r.index,
+ shallow_commits: r.shallow_commits,
}
}
}
diff --git a/vendor/gix/src/repository/init.rs b/vendor/gix/src/repository/init.rs
index ae6a42c3b..16659a013 100644
--- a/vendor/gix/src/repository/init.rs
+++ b/vendor/gix/src/repository/init.rs
@@ -1,6 +1,7 @@
use std::cell::RefCell;
impl crate::Repository {
+ #[allow(clippy::too_many_arguments)]
pub(crate) fn from_refs_and_objects(
refs: crate::RefStore,
objects: crate::OdbHandle,
@@ -9,6 +10,7 @@ impl crate::Repository {
config: crate::config::Cache,
linked_worktree_options: crate::open::Options,
index: crate::worktree::IndexStorage,
+ shallow_commits: crate::shallow::CommitsStorage,
) -> Self {
let objects = setup_objects(objects, &config);
crate::Repository {
@@ -20,6 +22,7 @@ impl crate::Repository {
config,
options: linked_worktree_options,
index,
+ shallow_commits,
}
}
diff --git a/vendor/gix/src/repository/mod.rs b/vendor/gix/src/repository/mod.rs
index 31199e22d..5b7a70d3b 100644
--- a/vendor/gix/src/repository/mod.rs
+++ b/vendor/gix/src/repository/mod.rs
@@ -19,17 +19,19 @@ impl crate::Repository {
}
}
+mod attributes;
mod cache;
mod config;
+mod excludes;
pub(crate) mod identity;
mod impls;
mod init;
mod location;
mod object;
-pub(crate) mod permissions;
mod reference;
mod remote;
mod revision;
+mod shallow;
mod snapshots;
mod state;
mod thread_safe;
diff --git a/vendor/gix/src/repository/object.rs b/vendor/gix/src/repository/object.rs
index bda1a54c3..f4592475f 100644
--- a/vendor/gix/src/repository/object.rs
+++ b/vendor/gix/src/repository/object.rs
@@ -1,5 +1,6 @@
#![allow(clippy::result_large_err)]
use std::convert::TryInto;
+use std::ops::DerefMut;
use gix_hash::ObjectId;
use gix_odb::{Find, FindExt, Write};
@@ -36,7 +37,7 @@ impl crate::Repository {
Ok(Object::from_data(id, kind, buf, self))
}
- /// Try to find the object with `id` or return `None` it it wasn't found.
+ /// Try to find the object with `id` or return `None` if it wasn't found.
pub fn try_find_object(&self, id: impl Into<ObjectId>) -> Result<Option<Object<'_>>, object::find::Error> {
let id = id.into();
if id == gix_hash::ObjectId::empty_tree(self.object_hash()) {
@@ -58,32 +59,71 @@ impl crate::Repository {
}
}
+ fn shared_empty_buf(&self) -> std::cell::RefMut<'_, Vec<u8>> {
+ let mut bufs = self.bufs.borrow_mut();
+ if bufs.last().is_none() {
+ bufs.push(Vec::with_capacity(512));
+ }
+ std::cell::RefMut::map(bufs, |bufs| {
+ let buf = bufs.last_mut().expect("we assure one is present");
+ buf.clear();
+ buf
+ })
+ }
+
/// Write the given object into the object database and return its object id.
+ ///
+ /// Note that we hash the object in memory to avoid storing objects that are already present. That way,
+ /// we avoid writing duplicate objects using slow disks that will eventually have to be garbage collected.
pub fn write_object(&self, object: impl gix_object::WriteTo) -> Result<Id<'_>, object::write::Error> {
+ let mut buf = self.shared_empty_buf();
+ object.write_to(buf.deref_mut())?;
+
+ let oid = gix_object::compute_hash(self.object_hash(), object.kind(), &buf);
+ if self.objects.contains(oid) {
+ return Ok(oid.attach(self));
+ }
+
self.objects
- .write(object)
+ .write_buf(object.kind(), &buf)
.map(|oid| oid.attach(self))
.map_err(Into::into)
}
/// Write a blob from the given `bytes`.
+ ///
+ /// We avoid writing duplicate objects to slow disks that will eventually have to be garbage collected by
+ /// pre-hashing the data, and checking if the object is already present.
pub fn write_blob(&self, bytes: impl AsRef<[u8]>) -> Result<Id<'_>, object::write::Error> {
+ let bytes = bytes.as_ref();
+ let oid = gix_object::compute_hash(self.object_hash(), gix_object::Kind::Blob, bytes);
+ if self.objects.contains(oid) {
+ return Ok(oid.attach(self));
+ }
self.objects
- .write_buf(gix_object::Kind::Blob, bytes.as_ref())
+ .write_buf(gix_object::Kind::Blob, bytes)
.map(|oid| oid.attach(self))
}
/// Write a blob from the given `Read` implementation.
+ ///
+ /// Note that we hash the object in memory to avoid storing objects that are already present. That way,
+ /// we avoid writing duplicate objects using slow disks that will eventually have to be garbage collected.
+ ///
+ /// If that is prohibitive, use the object database directly.
pub fn write_blob_stream(
&self,
mut bytes: impl std::io::Read + std::io::Seek,
) -> Result<Id<'_>, object::write::Error> {
- let current = bytes.stream_position()?;
- let len = bytes.seek(std::io::SeekFrom::End(0))? - current;
- bytes.seek(std::io::SeekFrom::Start(current))?;
+ let mut buf = self.shared_empty_buf();
+ std::io::copy(&mut bytes, buf.deref_mut())?;
+ let oid = gix_object::compute_hash(self.object_hash(), gix_object::Kind::Blob, &buf);
+ if self.objects.contains(oid) {
+ return Ok(oid.attach(self));
+ }
self.objects
- .write_stream(gix_object::Kind::Blob, len, bytes)
+ .write_buf(gix_object::Kind::Blob, &buf)
.map(|oid| oid.attach(self))
}
diff --git a/vendor/gix/src/repository/permissions.rs b/vendor/gix/src/repository/permissions.rs
index 88b61b739..633575a9d 100644
--- a/vendor/gix/src/repository/permissions.rs
+++ b/vendor/gix/src/repository/permissions.rs
@@ -1,14 +1,7 @@
+//! Various permissions to define what can be done when operating a [`Repository`][crate::Repository].
+use crate::open::Permissions;
use gix_sec::Trust;
-/// Permissions associated with various resources of a git repository
-#[derive(Debug, Clone)]
-pub struct Permissions {
- /// Permissions related to the environment
- pub env: Environment,
- /// Permissions related to the handling of git configuration.
- pub config: Config,
-}
-
/// Configure from which sources git configuration may be loaded.
///
/// Note that configuration from inside of the repository is always loaded as it's definitely required for correctness.
@@ -17,7 +10,7 @@ pub struct Config {
/// The git binary may come with configuration as part of its configuration, and if this is true (default false)
/// we will load the configuration of the git binary, if present and not a duplicate of the ones below.
///
- /// It's disable by default as it involves executing the git binary once per execution of the application.
+ /// It's disabled by default as it may involve executing the git binary once per execution of the application.
pub git_binary: bool,
/// Whether to use the system configuration.
/// This is defined as `$(prefix)/etc/gitconfig` on unix.
@@ -50,6 +43,18 @@ impl Config {
includes: true,
}
}
+
+ /// Load only configuration local to the git repository.
+ pub fn isolated() -> Self {
+ Config {
+ git_binary: false,
+ system: false,
+ git: false,
+ user: false,
+ env: false,
+ includes: false,
+ }
+ }
}
impl Default for Config {
@@ -58,8 +63,55 @@ impl Default for Config {
}
}
+/// Configure from which `gitattribute` files may be loaded.
+///
+/// Note that `.gitattribute` files from within the repository are always loaded.
+#[derive(Copy, Clone, Ord, PartialOrd, PartialEq, Eq, Debug, Hash)]
+pub struct Attributes {
+ /// The git binary may come with attribute configuration in its installation directory, and if this is true (default false)
+ /// we will load the configuration of the git binary.
+ ///
+ /// It's disabled by default as it involves executing the git binary once per execution of the application.
+ pub git_binary: bool,
+ /// Whether to use the system configuration.
+ /// This is typically defined as `$(prefix)/etc/gitconfig`.
+ pub system: bool,
+ /// Whether to use the git application configuration.
+ ///
+ /// A platform defined location for where a user's git application configuration should be located.
+ /// If `$XDG_CONFIG_HOME` is not set or empty, `$HOME/.config/git/attributes` will be used
+ /// on unix.
+ pub git: bool,
+}
+
+impl Attributes {
+ /// Allow everything which usually relates to a fully trusted environment
+ pub fn all() -> Self {
+ Attributes {
+ git_binary: false,
+ system: true,
+ git: true,
+ }
+ }
+
+ /// Allow loading attributes that are local to the git repository.
+ pub fn isolated() -> Self {
+ Attributes {
+ git_binary: false,
+ system: false,
+ git: false,
+ }
+ }
+}
+
+impl Default for Attributes {
+ fn default() -> Self {
+ Self::all()
+ }
+}
+
/// Permissions related to the usage of environment variables
-#[derive(Debug, Clone)]
+#[derive(Debug, Clone, Copy)]
pub struct Environment {
/// Control whether resources pointed to by `XDG_CONFIG_HOME` can be used when looking up common configuration values.
///
@@ -101,18 +153,29 @@ impl Environment {
objects: allow,
}
}
+
+ /// Don't allow loading any environment variables.
+ pub fn isolated() -> Self {
+ let deny = gix_sec::Permission::Deny;
+ Environment {
+ xdg_config_home: deny,
+ home: deny,
+ ssh_prefix: deny,
+ git_prefix: deny,
+ http_transport: deny,
+ identity: deny,
+ objects: deny,
+ }
+ }
}
impl Permissions {
- /// Return permissions that will not include configuration files not owned by the current user,
- /// but trust system and global configuration files along with those which are owned by the current user.
- ///
- /// This allows to read and write repositories even if they aren't owned by the current user, but avoid using
- /// anything else that could cause us to write into unknown locations or use programs beyond our `PATH`.
+ /// Secure permissions are similar to `all()`
pub fn secure() -> Self {
Permissions {
env: Environment::all(),
config: Config::all(),
+ attributes: Attributes::all(),
}
}
@@ -122,32 +185,16 @@ impl Permissions {
Permissions {
env: Environment::all(),
config: Config::all(),
+ attributes: Attributes::all(),
}
}
/// Don't read any but the local git configuration and deny reading any environment variables.
pub fn isolated() -> Self {
Permissions {
- config: Config {
- git_binary: false,
- system: false,
- git: false,
- user: false,
- env: false,
- includes: false,
- },
- env: {
- let deny = gix_sec::Permission::Deny;
- Environment {
- xdg_config_home: deny,
- home: deny,
- ssh_prefix: deny,
- git_prefix: deny,
- http_transport: deny,
- identity: deny,
- objects: deny,
- }
- },
+ config: Config::isolated(),
+ attributes: Attributes::isolated(),
+ env: Environment::isolated(),
}
}
}
diff --git a/vendor/gix/src/repository/shallow.rs b/vendor/gix/src/repository/shallow.rs
new file mode 100644
index 000000000..7fac83a55
--- /dev/null
+++ b/vendor/gix/src/repository/shallow.rs
@@ -0,0 +1,65 @@
+use std::{borrow::Cow, path::PathBuf};
+
+use crate::{
+ bstr::ByteSlice,
+ config::tree::{gitoxide, Key},
+ Repository,
+};
+
+impl Repository {
+ /// Return `true` if the repository is a shallow clone, i.e. contains history only up to a certain depth.
+ pub fn is_shallow(&self) -> bool {
+ self.shallow_file()
+ .metadata()
+ .map_or(false, |m| m.is_file() && m.len() > 0)
+ }
+
+ /// Return a shared list of shallow commits which is updated automatically if the in-memory snapshot has become stale
+ /// as the underlying file on disk has changed.
+ ///
+ /// The list of shallow commits represents the shallow boundary, beyond which we are lacking all (parent) commits.
+ /// Note that the list is never empty, as `Ok(None)` is returned in that case indicating the repository
+ /// isn't a shallow clone.
+ ///
+ /// The shared list is shared across all clones of this repository.
+ pub fn shallow_commits(&self) -> Result<Option<crate::shallow::Commits>, crate::shallow::open::Error> {
+ self.shallow_commits.recent_snapshot(
+ || self.shallow_file().metadata().ok().and_then(|m| m.modified().ok()),
+ || {
+ let buf = match std::fs::read(self.shallow_file()) {
+ Ok(buf) => buf,
+ Err(err) if err.kind() == std::io::ErrorKind::NotFound => return Ok(None),
+ Err(err) => return Err(err.into()),
+ };
+
+ let mut commits = buf
+ .lines()
+ .map(gix_hash::ObjectId::from_hex)
+ .collect::<Result<Vec<_>, _>>()?;
+
+ commits.sort();
+ if commits.is_empty() {
+ Ok(None)
+ } else {
+ Ok(Some(commits))
+ }
+ },
+ )
+ }
+
+ /// Return the path to the `shallow` file which contains hashes, one per line, that describe commits that don't have their
+ /// parents within this repository.
+ ///
+ /// Note that it may not exist if the repository isn't actually shallow.
+ pub fn shallow_file(&self) -> PathBuf {
+ let shallow_name = self
+ .config
+ .resolved
+ .string_filter_by_key(
+ gitoxide::Core::SHALLOW_FILE.logical_name().as_str(),
+ &mut self.filter_config_section(),
+ )
+ .unwrap_or_else(|| Cow::Borrowed("shallow".into()));
+ self.common_dir().join(gix_path::from_bstr(shallow_name))
+ }
+}
diff --git a/vendor/gix/src/repository/worktree.rs b/vendor/gix/src/repository/worktree.rs
index 2de31bc86..316009d29 100644
--- a/vendor/gix/src/repository/worktree.rs
+++ b/vendor/gix/src/repository/worktree.rs
@@ -1,6 +1,7 @@
+use crate::config::cache::util::ApplyLeniencyDefault;
use crate::{worktree, Worktree};
-/// Worktree iteration
+/// Interact with individual worktrees and their information.
impl crate::Repository {
/// Return a list of all _linked_ worktrees sorted by private git dir path as a lightweight proxy.
///
@@ -25,10 +26,6 @@ impl crate::Repository {
res.sort_by(|a, b| a.git_dir.cmp(&b.git_dir));
Ok(res)
}
-}
-
-/// Interact with individual worktrees and their information.
-impl crate::Repository {
/// Return the repository owning the main worktree, typically from a linked worktree.
///
/// Note that it might be the one that is currently open if this repository doesn't point to a linked worktree.
@@ -58,23 +55,14 @@ impl crate::Repository {
///
/// It will use the `index.threads` configuration key to learn how many threads to use.
/// Note that it may fail if there is no index.
- // TODO: test
pub fn open_index(&self) -> Result<gix_index::File, worktree::open_index::Error> {
let thread_limit = self
.config
.resolved
- .boolean("index", None, "threads")
- .map(|res| {
- res.map(|value| usize::from(!value)).or_else(|err| {
- gix_config::Integer::try_from(err.input.as_ref())
- .map_err(|err| worktree::open_index::Error::ConfigIndexThreads {
- value: err.input.clone(),
- err,
- })
- .map(|value| value.to_decimal().and_then(|v| v.try_into().ok()).unwrap_or(1))
- })
- })
- .transpose()?;
+ .string("index", None, "threads")
+ .map(|value| crate::config::tree::Index::THREADS.try_into_index_threads(value))
+ .transpose()
+ .with_lenient_default(self.config.lenient_config)?;
gix_index::File::at(
self.index_path(),
self.object_hash(),
diff --git a/vendor/gix/src/revision/spec/parse/types.rs b/vendor/gix/src/revision/spec/parse/types.rs
index 4e523ab14..d852c297e 100644
--- a/vendor/gix/src/revision/spec/parse/types.rs
+++ b/vendor/gix/src/revision/spec/parse/types.rs
@@ -1,7 +1,7 @@
use crate::{bstr::BString, object, reference};
/// A hint to know what to do if refs and object names are equal.
-#[derive(Debug, Copy, Clone, PartialEq, Eq)]
+#[derive(Default, Debug, Copy, Clone, PartialEq, Eq)]
pub enum RefsHint {
/// This is the default, and leads to specs that look like objects identified by full hex sha and are objects to be used
/// instead of similarly named references. The latter is not typical but can absolutely happen by accident.
@@ -9,6 +9,7 @@ pub enum RefsHint {
/// preferred as there are many valid object names like `beef` and `cafe` that are short and both valid and typical prefixes
/// for objects.
/// Git chooses this as default as well, even though it means that every object prefix is also looked up as ref.
+ #[default]
PreferObjectOnFullLengthHexShaUseRefOtherwise,
/// No matter what, if it looks like an object prefix and has an object, use it.
/// Note that no ref-lookup is made here which is the fastest option.
@@ -38,12 +39,6 @@ pub enum ObjectKindHint {
Blob,
}
-impl Default for RefsHint {
- fn default() -> Self {
- RefsHint::PreferObjectOnFullLengthHexShaUseRefOtherwise
- }
-}
-
/// Options for use in [`revision::Spec::from_bstr()`][crate::revision::Spec::from_bstr()].
#[derive(Debug, Default, Copy, Clone)]
pub struct Options {
@@ -177,6 +172,8 @@ pub enum Error {
},
#[error(transparent)]
Traverse(#[from] gix_traverse::commit::ancestors::Error),
+ #[error(transparent)]
+ Walk(#[from] crate::revision::walk::Error),
#[error("Spec does not contain a single object id")]
SingleNotFound,
}
diff --git a/vendor/gix/src/revision/walk.rs b/vendor/gix/src/revision/walk.rs
index 5b04b43a7..9c545d0d4 100644
--- a/vendor/gix/src/revision/walk.rs
+++ b/vendor/gix/src/revision/walk.rs
@@ -3,6 +3,16 @@ use gix_odb::FindExt;
use crate::{revision, Repository};
+/// The error returned by [`Platform::all()`].
+#[derive(Debug, thiserror::Error)]
+#[allow(missing_docs)]
+pub enum Error {
+ #[error(transparent)]
+ AncestorIter(#[from] gix_traverse::commit::ancestors::Error),
+ #[error(transparent)]
+ ShallowCommits(#[from] crate::shallow::open::Error),
+}
+
/// A platform to traverse the revision graph by adding starting points as well as points which shouldn't be crossed,
/// returned by [`Repository::rev_walk()`].
pub struct Platform<'repo> {
@@ -40,13 +50,14 @@ impl<'repo> Platform<'repo> {
/// Produce the iterator
impl<'repo> Platform<'repo> {
- /// Return an iterator to traverse all commits reachable as configured by the [Platform].
- ///
- /// # Performance
+ /// For each commit, let `filter` return `true` if it and its parents should be included in the traversal, or `false`
+ /// if the traversal should exclude it and its ancestry entirely.
///
- /// It's highly recommended to set an [`object cache`][Repository::object_cache_size()] on the parent repo
- /// to greatly speed up performance if the returned id is supposed to be looked up right after.
- pub fn all(self) -> Result<revision::Walk<'repo>, gix_traverse::commit::ancestors::Error> {
+ /// If `filter` is None, no pruning of the graph will be performed which is the default.
+ pub fn selected(
+ self,
+ mut filter: impl FnMut(&gix_hash::oid) -> bool + 'repo,
+ ) -> Result<revision::Walk<'repo>, Error> {
let Platform {
repo,
tips,
@@ -56,18 +67,52 @@ impl<'repo> Platform<'repo> {
Ok(revision::Walk {
repo,
inner: Box::new(
- gix_traverse::commit::Ancestors::new(
+ gix_traverse::commit::Ancestors::filtered(
tips,
gix_traverse::commit::ancestors::State::default(),
move |oid, buf| repo.objects.find_commit_iter(oid, buf),
+ {
+ let shallow_commits = repo.shallow_commits()?;
+ let mut grafted_parents_to_skip = Vec::new();
+ let mut buf = Vec::new();
+ move |id| {
+ if !filter(id) {
+ return false;
+ }
+ match shallow_commits.as_ref() {
+ Some(commits) => {
+ let id = id.to_owned();
+ if let Ok(idx) = grafted_parents_to_skip.binary_search(&id) {
+ grafted_parents_to_skip.remove(idx);
+ return false;
+ };
+ if commits.binary_search(&id).is_ok() {
+ if let Ok(commit) = repo.objects.find_commit_iter(id, &mut buf) {
+ grafted_parents_to_skip.extend(commit.parent_ids());
+ grafted_parents_to_skip.sort();
+ }
+ };
+ true
+ }
+ None => true,
+ }
+ }
+ },
)
.sorting(sorting)?
.parents(parents),
),
- is_shallow: None,
- error_on_missing_commit: false,
})
}
+ /// Return an iterator to traverse all commits reachable as configured by the [Platform].
+ ///
+ /// # Performance
+ ///
+ /// It's highly recommended to set an [`object cache`][Repository::object_cache_size()] on the parent repo
+ /// to greatly speed up performance if the returned id is supposed to be looked up right after.
+ pub fn all(self) -> Result<revision::Walk<'repo>, Error> {
+ self.selected(|_| true)
+ }
}
pub(crate) mod iter {
@@ -78,50 +123,13 @@ pub(crate) mod iter {
pub(crate) repo: &'repo crate::Repository,
pub(crate) inner:
Box<dyn Iterator<Item = Result<gix_hash::ObjectId, gix_traverse::commit::ancestors::Error>> + 'repo>,
- pub(crate) error_on_missing_commit: bool,
- // TODO: tests
- /// After iteration this flag is true if the iteration was stopped prematurely due to missing parent commits.
- /// Note that this flag won't be `Some` if any iteration error occurs, which is the case if
- /// [`error_on_missing_commit()`][Walk::error_on_missing_commit()] was called.
- ///
- /// This happens if a repository is a shallow clone.
- /// Note that this value is `None` as long as the iteration isn't complete.
- pub is_shallow: Option<bool>,
- }
-
- impl<'repo> Walk<'repo> {
- // TODO: tests
- /// Once invoked, the iteration will return an error if a commit cannot be found in the object database. This typically happens
- /// when operating on a shallow clone and thus is non-critical by default.
- ///
- /// Check the [`is_shallow`][Walk::is_shallow] field once the iteration ended otherwise to learn if a shallow commit graph
- /// was encountered.
- pub fn error_on_missing_commit(mut self) -> Self {
- self.error_on_missing_commit = true;
- self
- }
}
impl<'repo> Iterator for Walk<'repo> {
type Item = Result<Id<'repo>, gix_traverse::commit::ancestors::Error>;
fn next(&mut self) -> Option<Self::Item> {
- match self.inner.next() {
- None => {
- self.is_shallow = Some(false);
- None
- }
- Some(Ok(oid)) => Some(Ok(oid.attach(self.repo))),
- Some(Err(err @ gix_traverse::commit::ancestors::Error::FindExisting { .. })) => {
- if self.error_on_missing_commit {
- Some(Err(err))
- } else {
- self.is_shallow = Some(true);
- None
- }
- }
- Some(Err(err)) => Some(Err(err)),
- }
+ self.inner.next().map(|res| res.map(|id| id.attach(self.repo)))
}
}
}
diff --git a/vendor/gix/src/shallow.rs b/vendor/gix/src/shallow.rs
new file mode 100644
index 000000000..d49653a65
--- /dev/null
+++ b/vendor/gix/src/shallow.rs
@@ -0,0 +1,92 @@
+pub(crate) type CommitsStorage =
+ gix_features::threading::OwnShared<gix_fs::SharedFileSnapshotMut<Vec<gix_hash::ObjectId>>>;
+/// A lazily loaded and auto-updated list of commits which are at the shallow boundary (behind which there are no commits available),
+/// sorted to allow bisecting.
+pub type Commits = gix_fs::SharedFileSnapshot<Vec<gix_hash::ObjectId>>;
+
+///
+#[cfg(any(feature = "blocking-network-client", feature = "async-network-client"))]
+pub mod write {
+ pub(crate) mod function {
+ use std::io::Write;
+
+ use gix_protocol::fetch::response::ShallowUpdate;
+
+ use crate::shallow::{write::Error, Commits};
+
+ /// Write the previously obtained (possibly non-existing) `shallow_commits` to the shallow `file`
+ /// after applying all `updates`.
+ ///
+ /// If this leaves the list of shallow commits empty, the file is removed.
+ ///
+ /// ### Deviation
+ ///
+ /// Git also prunes the set of shallow commits while writing, we don't until we support some sort of pruning.
+ pub fn write(
+ mut file: gix_lock::File,
+ shallow_commits: Option<Commits>,
+ updates: &[ShallowUpdate],
+ ) -> Result<(), Error> {
+ let mut shallow_commits = shallow_commits.map(|sc| (**sc).to_owned()).unwrap_or_default();
+ for update in updates {
+ match update {
+ ShallowUpdate::Shallow(id) => {
+ shallow_commits.push(*id);
+ }
+ ShallowUpdate::Unshallow(id) => shallow_commits.retain(|oid| oid != id),
+ }
+ }
+ if shallow_commits.is_empty() {
+ std::fs::remove_file(file.resource_path())?;
+ drop(file);
+ return Ok(());
+ }
+
+ if shallow_commits.is_empty() {
+ if let Err(err) = std::fs::remove_file(file.resource_path()) {
+ if err.kind() != std::io::ErrorKind::NotFound {
+ return Err(err.into());
+ }
+ }
+ } else {
+ shallow_commits.sort();
+ let mut buf = Vec::<u8>::new();
+ for commit in shallow_commits {
+ commit.write_hex_to(&mut buf).map_err(Error::Io)?;
+ buf.push(b'\n');
+ }
+ file.write_all(&buf).map_err(Error::Io)?;
+ file.flush()?;
+ }
+ file.commit()?;
+ Ok(())
+ }
+ }
+
+ /// The error returned by [`write()`][crate::shallow::write()].
+ #[derive(Debug, thiserror::Error)]
+ #[allow(missing_docs)]
+ pub enum Error {
+ #[error(transparent)]
+ Commit(#[from] gix_lock::commit::Error<gix_lock::File>),
+ #[error("Could not remove an empty shallow file")]
+ RemoveEmpty(#[from] std::io::Error),
+ #[error("Failed to write object id to shallow file")]
+ Io(std::io::Error),
+ }
+}
+#[cfg(any(feature = "blocking-network-client", feature = "async-network-client"))]
+pub use write::function::write;
+
+///
+pub mod open {
+ /// The error returned by [`Repository::shallow_commits()`][crate::Repository::shallow_commits()].
+ #[derive(Debug, thiserror::Error)]
+ #[allow(missing_docs)]
+ pub enum Error {
+ #[error("Could not open shallow file for reading")]
+ Io(#[from] std::io::Error),
+ #[error("Could not decode a line in shallow file as hex-encoded object hash")]
+ DecodeHash(#[from] gix_hash::decode::Error),
+ }
+}
diff --git a/vendor/gix/src/state.rs b/vendor/gix/src/state.rs
new file mode 100644
index 000000000..d8ee99835
--- /dev/null
+++ b/vendor/gix/src/state.rs
@@ -0,0 +1,25 @@
+/// Tell what operation is currently in progress.
+#[derive(Debug, PartialEq, Eq)]
+pub enum InProgress {
+ /// A mailbox is being applied.
+ ApplyMailbox,
+ /// A rebase is happening while a mailbox is being applied.
+ // TODO: test
+ ApplyMailboxRebase,
+ /// A git bisect operation has not yet been concluded.
+ Bisect,
+ /// A cherry pick operation.
+ CherryPick,
+ /// A cherry pick with multiple commits pending.
+ CherryPickSequence,
+ /// A merge operation.
+ Merge,
+ /// A rebase operation.
+ Rebase,
+ /// An interactive rebase operation.
+ RebaseInteractive,
+ /// A revert operation.
+ Revert,
+ /// A revert operation with multiple commits pending.
+ RevertSequence,
+}
diff --git a/vendor/gix/src/types.rs b/vendor/gix/src/types.rs
index 34ffdc8bf..eafa6a3f8 100644
--- a/vendor/gix/src/types.rs
+++ b/vendor/gix/src/types.rs
@@ -152,6 +152,7 @@ pub struct Repository {
/// Particularly useful when following linked worktrees and instantiating new equally configured worktree repositories.
pub(crate) options: crate::open::Options,
pub(crate) index: crate::worktree::IndexStorage,
+ pub(crate) shallow_commits: crate::shallow::CommitsStorage,
}
/// An instance with access to everything a git repository entails, best imagined as container implementing `Sync + Send` for _most_
@@ -175,6 +176,7 @@ pub struct ThreadSafeRepository {
pub(crate) linked_worktree_options: crate::open::Options,
/// The index of this instances worktree.
pub(crate) index: crate::worktree::IndexStorage,
+ pub(crate) shallow_commits: crate::shallow::CommitsStorage,
}
/// A remote which represents a way to interact with hosts for remote clones of the parent repository.
diff --git a/vendor/gix/src/worktree/mod.rs b/vendor/gix/src/worktree/mod.rs
index 19a44a900..8db123554 100644
--- a/vendor/gix/src/worktree/mod.rs
+++ b/vendor/gix/src/worktree/mod.rs
@@ -7,9 +7,9 @@ use crate::{
Repository,
};
-pub(crate) type IndexStorage = gix_features::threading::OwnShared<gix_features::fs::MutableSnapshot<gix_index::File>>;
+pub(crate) type IndexStorage = gix_features::threading::OwnShared<gix_fs::SharedFileSnapshotMut<gix_index::File>>;
/// A lazily loaded and auto-updated worktree index.
-pub type Index = gix_features::fs::SharedSnapshot<gix_index::File>;
+pub type Index = gix_fs::SharedFileSnapshot<gix_index::File>;
/// A stand-in to a worktree as result of a worktree iteration.
///
@@ -71,18 +71,12 @@ pub mod proxy;
///
pub mod open_index {
- use crate::bstr::BString;
-
/// The error returned by [`Worktree::open_index()`][crate::Worktree::open_index()].
#[derive(Debug, thiserror::Error)]
#[allow(missing_docs)]
pub enum Error {
- #[error("Could not interpret value '{}' as 'index.threads'", .value)]
- ConfigIndexThreads {
- value: BString,
- #[source]
- err: gix_config::value::Error,
- },
+ #[error(transparent)]
+ ConfigIndexThreads(#[from] crate::config::key::GenericErrorWithValue),
#[error(transparent)]
IndexFile(#[from] gix_index::file::init::Error),
}
@@ -102,59 +96,64 @@ pub mod open_index {
///
pub mod excludes {
- use std::path::PathBuf;
-
/// The error returned by [`Worktree::excludes()`][crate::Worktree::excludes()].
#[derive(Debug, thiserror::Error)]
#[allow(missing_docs)]
pub enum Error {
- #[error("Could not read repository exclude.")]
- Io(#[from] std::io::Error),
#[error(transparent)]
- EnvironmentPermission(#[from] gix_sec::permission::Error<PathBuf>),
- #[error("The value for `core.excludesFile` could not be read from configuration")]
- ExcludesFilePathInterpolation(#[from] gix_config::path::interpolate::Error),
+ OpenIndex(#[from] crate::worktree::open_index::Error),
+ #[error(transparent)]
+ CreateCache(#[from] crate::config::exclude_stack::Error),
}
impl<'repo> crate::Worktree<'repo> {
/// Configure a file-system cache checking if files below the repository are excluded.
///
- /// This takes into consideration all the usual repository configuration.
- // TODO: test, provide higher-level interface that is much easier to use and doesn't panic.
- pub fn excludes(
- &self,
- index: &gix_index::State,
- overrides: Option<gix_attributes::MatchGroup<gix_attributes::Ignore>>,
- ) -> Result<gix_worktree::fs::Cache, Error> {
- let repo = self.parent;
- let case = repo
- .config
- .ignore_case
- .then_some(gix_glob::pattern::Case::Fold)
- .unwrap_or_default();
- let mut buf = Vec::with_capacity(512);
- let excludes_file = match repo.config.excludes_file().transpose()? {
- Some(user_path) => Some(user_path),
- None => repo.config.xdg_config_path("ignore")?,
- };
- let state = gix_worktree::fs::cache::State::IgnoreStack(gix_worktree::fs::cache::state::Ignore::new(
- overrides.unwrap_or_default(),
- gix_attributes::MatchGroup::<gix_attributes::Ignore>::from_git_dir(
- repo.git_dir(),
- excludes_file,
- &mut buf,
- )?,
- None,
- case,
- ));
- let attribute_list = state.build_attribute_list(index, index.path_backing(), case);
- Ok(gix_worktree::fs::Cache::new(
- self.path,
- state,
- case,
- buf,
- attribute_list,
- ))
+ /// This takes into consideration all the usual repository configuration, namely:
+ ///
+ /// * `$XDG_CONFIG_HOME/…/ignore` if `core.excludesFile` is *not* set, otherwise use the configured file.
+ /// * `$GIT_DIR/info/exclude` if present.
+ ///
+ /// When only excludes are desired, this is the most efficient way to obtain them. Otherwise use
+ /// [`Worktree::attributes()`][crate::Worktree::attributes()] for accessing both attributes and excludes.
+ pub fn excludes(&self, overrides: Option<gix_ignore::Search>) -> Result<gix_worktree::Cache, Error> {
+ let index = self.index()?;
+ Ok(self.parent.excludes(
+ &index,
+ overrides,
+ gix_worktree::cache::state::ignore::Source::WorktreeThenIdMappingIfNotSkipped,
+ )?)
+ }
+ }
+}
+
+///
+pub mod attributes {
+ /// The error returned by [`Worktree::attributes()`][crate::Worktree::attributes()].
+ #[derive(Debug, thiserror::Error)]
+ #[allow(missing_docs)]
+ pub enum Error {
+ #[error(transparent)]
+ OpenIndex(#[from] crate::worktree::open_index::Error),
+ #[error(transparent)]
+ CreateCache(#[from] crate::attributes::Error),
+ }
+
+ impl<'repo> crate::Worktree<'repo> {
+ /// Configure a file-system cache checking if files below the repository are excluded or for querying their attributes.
+ ///
+ /// This takes into consideration all the usual repository configuration, namely:
+ ///
+ /// * `$XDG_CONFIG_HOME/…/ignore|attributes` if `core.excludesFile|attributesFile` is *not* set, otherwise use the configured file.
+ /// * `$GIT_DIR/info/exclude|attributes` if present.
+ pub fn attributes(&self, overrides: Option<gix_ignore::Search>) -> Result<gix_worktree::Cache, Error> {
+ let index = self.index()?;
+ Ok(self.parent.attributes(
+ &index,
+ gix_worktree::cache::state::attributes::Source::WorktreeThenIdMapping,
+ gix_worktree::cache::state::ignore::Source::WorktreeThenIdMappingIfNotSkipped,
+ overrides,
+ )?)
}
}
}