summaryrefslogtreecommitdiffstats
path: root/vendor/gix/src/repository
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-30 18:31:44 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-30 18:31:44 +0000
commitc23a457e72abe608715ac76f076f47dc42af07a5 (patch)
tree2772049aaf84b5c9d0ed12ec8d86812f7a7904b6 /vendor/gix/src/repository
parentReleasing progress-linux version 1.73.0+dfsg1-1~progress7.99u1. (diff)
downloadrustc-c23a457e72abe608715ac76f076f47dc42af07a5.tar.xz
rustc-c23a457e72abe608715ac76f076f47dc42af07a5.zip
Merging upstream version 1.74.1+dfsg1.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'vendor/gix/src/repository')
-rw-r--r--vendor/gix/src/repository/attributes.rs119
-rw-r--r--vendor/gix/src/repository/config/mod.rs15
-rw-r--r--vendor/gix/src/repository/config/transport.rs5
-rw-r--r--vendor/gix/src/repository/excludes.rs45
-rw-r--r--vendor/gix/src/repository/filter.rs64
-rw-r--r--vendor/gix/src/repository/graph.rs26
-rw-r--r--vendor/gix/src/repository/identity.rs20
-rw-r--r--vendor/gix/src/repository/impls.rs12
-rw-r--r--vendor/gix/src/repository/index.rs133
-rw-r--r--vendor/gix/src/repository/init.rs17
-rw-r--r--vendor/gix/src/repository/location.rs42
-rw-r--r--vendor/gix/src/repository/mailmap.rs (renamed from vendor/gix/src/repository/snapshots.rs)3
-rw-r--r--vendor/gix/src/repository/mod.rs94
-rw-r--r--vendor/gix/src/repository/object.rs105
-rw-r--r--vendor/gix/src/repository/pathspec.rs55
-rw-r--r--vendor/gix/src/repository/reference.rs29
-rw-r--r--vendor/gix/src/repository/remote.rs42
-rw-r--r--vendor/gix/src/repository/revision.rs5
-rw-r--r--vendor/gix/src/repository/submodule.rs96
-rw-r--r--vendor/gix/src/repository/worktree.rs137
20 files changed, 869 insertions, 195 deletions
diff --git a/vendor/gix/src/repository/attributes.rs b/vendor/gix/src/repository/attributes.rs
index 252529761..7f747f7fd 100644
--- a/vendor/gix/src/repository/attributes.rs
+++ b/vendor/gix/src/repository/attributes.rs
@@ -1,5 +1,15 @@
//! exclude information
-use crate::Repository;
+use crate::{config, AttributeStack, Repository};
+
+/// The error returned by [`Repository::attributes()`].
+#[derive(Debug, thiserror::Error)]
+#[allow(missing_docs)]
+pub enum Error {
+ #[error(transparent)]
+ ConfigureAttributes(#[from] config::attribute_stack::Error),
+ #[error(transparent)]
+ ConfigureExcludes(#[from] config::exclude_stack::Error),
+}
impl Repository {
/// Configure a file-system cache for accessing git attributes *and* excludes on a per-path basis.
@@ -14,15 +24,14 @@ impl Repository {
///
/// * `$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.
+ #[cfg(feature = "attributes")]
pub fn attributes(
&self,
index: &gix_index::State,
- attributes_source: gix_worktree::cache::state::attributes::Source,
- ignore_source: gix_worktree::cache::state::ignore::Source,
+ attributes_source: gix_worktree::stack::state::attributes::Source,
+ ignore_source: gix_worktree::stack::state::ignore::Source,
exclude_overrides: Option<gix_ignore::Search>,
- ) -> Result<gix_worktree::Cache, crate::attributes::Error> {
+ ) -> Result<AttributeStack<'_>, Error> {
let case = if self.config.ignore_case {
gix_glob::pattern::Case::Fold
} else {
@@ -36,15 +45,95 @@ impl Repository {
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,
+ let state = gix_worktree::stack::State::AttributesAndIgnoreStack { attributes, ignore };
+ let attribute_list = state.id_mappings_from_index(index, index.path_backing(), case);
+ Ok(AttributeStack::new(
+ gix_worktree::Stack::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,
+ ),
+ self,
+ ))
+ }
+
+ /// Like [attributes()][Self::attributes()], but without access to exclude/ignore information.
+ #[cfg(feature = "attributes")]
+ pub fn attributes_only(
+ &self,
+ index: &gix_index::State,
+ attributes_source: gix_worktree::stack::state::attributes::Source,
+ ) -> Result<AttributeStack<'_>, config::attribute_stack::Error> {
+ let case = if self.config.ignore_case {
+ gix_glob::pattern::Case::Fold
+ } else {
+ gix_glob::pattern::Case::Sensitive
+ };
+ let (attributes, buf) = self.config.assemble_attribute_globals(
+ self.git_dir(),
+ attributes_source,
+ self.options.permissions.attributes,
+ )?;
+ let state = gix_worktree::stack::State::AttributesStack(attributes);
+ let attribute_list = state.id_mappings_from_index(index, index.path_backing(), case);
+ Ok(AttributeStack::new(
+ gix_worktree::Stack::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,
+ ),
+ self,
+ ))
+ }
+
+ /// 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
+ #[cfg(feature = "excludes")]
+ pub fn excludes(
+ &self,
+ index: &gix_index::State,
+ overrides: Option<gix_ignore::Search>,
+ source: gix_worktree::stack::state::ignore::Source,
+ ) -> Result<AttributeStack<'_>, 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::stack::State::IgnoreStack(ignore);
+ let attribute_list = state.id_mappings_from_index(index, index.path_backing(), case);
+ Ok(AttributeStack::new(
+ gix_worktree::Stack::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,
+ ),
+ self,
))
}
}
diff --git a/vendor/gix/src/repository/config/mod.rs b/vendor/gix/src/repository/config/mod.rs
index e5c8b64f3..618ccf0f6 100644
--- a/vendor/gix/src/repository/config/mod.rs
+++ b/vendor/gix/src/repository/config/mod.rs
@@ -22,6 +22,21 @@ impl crate::Repository {
}
}
+ /// Return filesystem options as retrieved from the repository configuration.
+ ///
+ /// Note that these values have not been [probed](gix_fs::Capabilities::probe()).
+ pub fn filesystem_options(&self) -> Result<gix_fs::Capabilities, config::boolean::Error> {
+ self.config.fs_capabilities()
+ }
+
+ /// Return filesystem options on how to perform stat-checks, typically in relation to the index.
+ ///
+ /// Note that these values have not been [probed](gix_fs::Capabilities::probe()).
+ #[cfg(feature = "index")]
+ pub fn stat_options(&self) -> Result<gix_index::entry::stat::Options, config::stat_options::Error> {
+ self.config.stat_options()
+ }
+
/// The options used to open the repository.
pub fn open_options(&self) -> &crate::open::Options {
&self.options
diff --git a/vendor/gix/src/repository/config/transport.rs b/vendor/gix/src/repository/config/transport.rs
index dcfbc0bf6..99b5a7f47 100644
--- a/vendor/gix/src/repository/config/transport.rs
+++ b/vendor/gix/src/repository/config/transport.rs
@@ -1,6 +1,8 @@
#![allow(clippy::result_large_err)]
use std::any::Any;
+use gix_macros::momo;
+
use crate::bstr::BStr;
impl crate::Repository {
@@ -21,6 +23,7 @@ impl crate::Repository {
)),
allow(unused_variables)
)]
+ #[momo]
pub fn transport_options<'a>(
&self,
url: impl Into<&'a BStr>,
@@ -359,7 +362,7 @@ impl crate::Repository {
self.install_dir().ok().as_deref(),
self.config.home_dir().as_deref(),
))
- .map(|cow| cow.into_owned())
+ .map(std::borrow::Cow::into_owned)
})
.transpose()
.with_leniency(lenient)
diff --git a/vendor/gix/src/repository/excludes.rs b/vendor/gix/src/repository/excludes.rs
deleted file mode 100644
index 6281549e0..000000000
--- a/vendor/gix/src/repository/excludes.rs
+++ /dev/null
@@ -1,45 +0,0 @@
-//! 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/filter.rs b/vendor/gix/src/repository/filter.rs
new file mode 100644
index 000000000..3aacb1a3d
--- /dev/null
+++ b/vendor/gix/src/repository/filter.rs
@@ -0,0 +1,64 @@
+use crate::{filter, repository::IndexPersistedOrInMemory, Id, Repository};
+
+///
+pub mod pipeline {
+ /// The error returned by [Repository::filter_pipeline()][super::Repository::filter_pipeline()].
+ #[derive(Debug, thiserror::Error)]
+ #[allow(missing_docs)]
+ pub enum Error {
+ #[error("Could not obtain head commit of bare repository")]
+ HeadCommit(#[from] crate::reference::head_commit::Error),
+ #[error(transparent)]
+ DecodeCommit(#[from] gix_object::decode::Error),
+ #[error("Could not create index from tree at HEAD^{{tree}}")]
+ TreeTraverse(#[from] gix_traverse::tree::breadthfirst::Error),
+ #[error(transparent)]
+ BareAttributes(#[from] crate::config::attribute_stack::Error),
+ #[error(transparent)]
+ WorktreeIndex(#[from] crate::worktree::open_index::Error),
+ #[error(transparent)]
+ Init(#[from] crate::filter::pipeline::options::Error),
+ }
+}
+
+impl Repository {
+ /// Configure a pipeline for converting byte buffers to the worktree representation, and byte streams to the git-internal
+ /// representation. Also return the index that was used when initializing the pipeline as it may be useful when calling
+ /// [convert_to_git()][filter::Pipeline::convert_to_git()].
+ /// Bare repositories will either use `HEAD^{tree}` for accessing all relevant worktree files or the given `tree_if_bare`.
+ ///
+ /// Note that this is considered a primitive as it operates on data directly and will not have permanent effects.
+ /// We also return the index that was used to configure the attributes cache (for accessing `.gitattributes`), which can be reused
+ /// after it was possibly created from a tree, an expensive operation.
+ ///
+ /// ### Performance
+ ///
+ /// Note that when in a repository with worktree, files in the worktree will be read with priority, which causes at least a stat
+ /// each time the directory is changed. This can be expensive if access isn't in sorted order, which would cause more then necessary
+ /// stats: one per directory.
+ pub fn filter_pipeline(
+ &self,
+ tree_if_bare: Option<gix_hash::ObjectId>,
+ ) -> Result<(filter::Pipeline<'_>, IndexPersistedOrInMemory), pipeline::Error> {
+ let (cache, index) = if self.is_bare() {
+ let index = self.index_from_tree(&tree_if_bare.map_or_else(
+ || {
+ self.head_commit()
+ .map_err(pipeline::Error::from)
+ .and_then(|c| c.tree_id().map(Id::detach).map_err(Into::into))
+ },
+ Ok,
+ )?)?;
+ let cache = self.attributes_only(&index, gix_worktree::stack::state::attributes::Source::IdMapping)?;
+ (cache, IndexPersistedOrInMemory::InMemory(index))
+ } else {
+ let index = self.index()?;
+ let cache = self.attributes_only(
+ &index,
+ gix_worktree::stack::state::attributes::Source::WorktreeThenIdMapping,
+ )?;
+ (cache, IndexPersistedOrInMemory::Persisted(index))
+ };
+ Ok((filter::Pipeline::new(self, cache.detach())?, index))
+ }
+}
diff --git a/vendor/gix/src/repository/graph.rs b/vendor/gix/src/repository/graph.rs
index a1f6c7f89..f4f2b18cc 100644
--- a/vendor/gix/src/repository/graph.rs
+++ b/vendor/gix/src/repository/graph.rs
@@ -7,18 +7,34 @@ impl crate::Repository {
/// Note that the commitgraph will be used if it is present and readable, but it won't be an error if it is corrupted. In that case,
/// it will just not be used.
///
+ /// Note that a commitgraph is only allowed to be used if `core.commitGraph` is true (the default), and that configuration errors are
+ /// ignored as well.
+ ///
/// ### Performance
///
- /// Note that the [Graph][gix_revision::Graph] can be sensitive to various object database settings that may affect the performance
+ /// Note that the [Graph][gix_revwalk::Graph] can be sensitive to various object database settings that may affect the performance
/// of the commit walk.
- pub fn commit_graph<T>(&self) -> gix_revision::Graph<'_, T> {
- gix_revision::Graph::new(
+ pub fn revision_graph<T>(&self) -> gix_revwalk::Graph<'_, T> {
+ gix_revwalk::Graph::new(
|id, buf| {
self.objects
.try_find(id, buf)
- .map(|r| r.and_then(|d| d.try_into_commit_iter()))
+ .map(|r| r.and_then(gix_object::Data::try_into_commit_iter))
},
- gix_commitgraph::at(self.objects.store_ref().path().join("info")).ok(),
+ self.config
+ .may_use_commit_graph()
+ .unwrap_or(true)
+ .then(|| gix_commitgraph::at(self.objects.store_ref().path().join("info")).ok())
+ .flatten(),
)
}
+
+ /// Return a cache for commits and their graph structure, as managed by `git commit-graph`, for accelerating commit walks on
+ /// a low level.
+ ///
+ /// Note that [`revision_graph()`][crate::Repository::revision_graph()] should be preferred for general purpose walks that don't
+ /// rely on the actual commit cache to be present, while leveraging it if possible.
+ pub fn commit_graph(&self) -> Result<gix_commitgraph::Graph, gix_commitgraph::init::Error> {
+ gix_commitgraph::at(self.objects.store_ref().path().join("info"))
+ }
}
diff --git a/vendor/gix/src/repository/identity.rs b/vendor/gix/src/repository/identity.rs
index 61a4b4a98..a4e39089e 100644
--- a/vendor/gix/src/repository/identity.rs
+++ b/vendor/gix/src/repository/identity.rs
@@ -31,13 +31,13 @@ impl crate::Repository {
let p = self.config.personas();
Ok(gix_actor::SignatureRef {
- name: p.committer.name.as_ref().or(p.user.name.as_ref()).map(|v| v.as_ref())?,
+ name: p.committer.name.as_ref().or(p.user.name.as_ref()).map(AsRef::as_ref)?,
email: p
.committer
.email
.as_ref()
.or(p.user.email.as_ref())
- .map(|v| v.as_ref())?,
+ .map(AsRef::as_ref)?,
time: match extract_time_or_default(p.committer.time.as_ref(), &gitoxide::Commit::COMMITTER_DATE) {
Ok(t) => t,
Err(err) => return Some(Err(err)),
@@ -61,8 +61,8 @@ impl crate::Repository {
let p = self.config.personas();
Ok(gix_actor::SignatureRef {
- name: p.author.name.as_ref().or(p.user.name.as_ref()).map(|v| v.as_ref())?,
- email: p.author.email.as_ref().or(p.user.email.as_ref()).map(|v| v.as_ref())?,
+ name: p.author.name.as_ref().or(p.user.name.as_ref()).map(AsRef::as_ref)?,
+ email: p.author.email.as_ref().or(p.user.email.as_ref()).map(AsRef::as_ref)?,
time: match extract_time_or_default(p.author.time.as_ref(), &gitoxide::Commit::AUTHOR_DATE) {
Ok(t) => t,
Err(err) => return Some(Err(err)),
@@ -73,9 +73,9 @@ impl crate::Repository {
}
fn extract_time_or_default(
- time: Option<&Result<gix_actor::Time, gix_date::parse::Error>>,
+ time: Option<&Result<gix_date::Time, gix_date::parse::Error>>,
config_key: &'static keys::Time,
-) -> Result<gix_actor::Time, config::time::Error> {
+) -> Result<gix_date::Time, config::time::Error> {
match time {
Some(Ok(t)) => Ok(*t),
None => Ok(gix_date::Time::now_local_or_utc()),
@@ -88,7 +88,7 @@ pub(crate) struct Entity {
pub name: Option<BString>,
pub email: Option<BString>,
/// A time parsed from an environment variable, handling potential errors is delayed.
- pub time: Option<Result<gix_actor::Time, gix_date::parse::Error>>,
+ pub time: Option<Result<gix_date::Time, gix_date::parse::Error>>,
}
#[derive(Debug, Clone)]
@@ -117,11 +117,11 @@ impl Personas {
config
.string(name_key.section.name(), None, name_key.name)
.or_else(|| fallback.as_ref().and_then(|(s, name_key, _)| s.value(name_key.name)))
- .map(|v| v.into_owned()),
+ .map(std::borrow::Cow::into_owned),
config
.string(email_key.section.name(), None, email_key.name)
.or_else(|| fallback.as_ref().and_then(|(s, _, email_key)| s.value(email_key.name)))
- .map(|v| v.into_owned()),
+ .map(std::borrow::Cow::into_owned),
)
}
let now = SystemTime::now();
@@ -152,7 +152,7 @@ impl Personas {
user_email = user_email.or_else(|| {
config
.string_by_key(gitoxide::User::EMAIL_FALLBACK.logical_name().as_str())
- .map(|v| v.into_owned())
+ .map(std::borrow::Cow::into_owned)
});
Personas {
user: Entity {
diff --git a/vendor/gix/src/repository/impls.rs b/vendor/gix/src/repository/impls.rs
index 5da55290c..36fd788dc 100644
--- a/vendor/gix/src/repository/impls.rs
+++ b/vendor/gix/src/repository/impls.rs
@@ -7,8 +7,11 @@ impl Clone for crate::Repository {
self.common_dir.clone(),
self.config.clone(),
self.options.clone(),
+ #[cfg(feature = "index")]
self.index.clone(),
self.shallow_commits.clone(),
+ #[cfg(feature = "attributes")]
+ self.modules.clone(),
)
}
}
@@ -40,8 +43,11 @@ impl From<&crate::ThreadSafeRepository> for crate::Repository {
repo.common_dir.clone(),
repo.config.clone(),
repo.linked_worktree_options.clone(),
+ #[cfg(feature = "index")]
repo.index.clone(),
repo.shallow_commits.clone(),
+ #[cfg(feature = "attributes")]
+ repo.modules.clone(),
)
}
}
@@ -55,8 +61,11 @@ impl From<crate::ThreadSafeRepository> for crate::Repository {
repo.common_dir,
repo.config,
repo.linked_worktree_options,
+ #[cfg(feature = "index")]
repo.index,
repo.shallow_commits,
+ #[cfg(feature = "attributes")]
+ repo.modules.clone(),
)
}
}
@@ -70,7 +79,10 @@ impl From<crate::Repository> for crate::ThreadSafeRepository {
common_dir: r.common_dir,
config: r.config,
linked_worktree_options: r.options,
+ #[cfg(feature = "index")]
index: r.index,
+ #[cfg(feature = "attributes")]
+ modules: r.modules,
shallow_commits: r.shallow_commits,
}
}
diff --git a/vendor/gix/src/repository/index.rs b/vendor/gix/src/repository/index.rs
new file mode 100644
index 000000000..a21b138a5
--- /dev/null
+++ b/vendor/gix/src/repository/index.rs
@@ -0,0 +1,133 @@
+use gix_odb::FindExt;
+
+use crate::{config::cache::util::ApplyLeniencyDefault, repository::IndexPersistedOrInMemory, worktree};
+
+/// Index access
+impl crate::Repository {
+ /// Open a new copy of the index file and decode it entirely.
+ ///
+ /// 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.
+ pub fn open_index(&self) -> Result<gix_index::File, worktree::open_index::Error> {
+ let thread_limit = self
+ .config
+ .resolved
+ .string("index", None, "threads")
+ .map(|value| crate::config::tree::Index::THREADS.try_into_index_threads(value))
+ .transpose()
+ .with_lenient_default(self.config.lenient_config)?;
+ let skip_hash = self
+ .config
+ .resolved
+ .boolean("index", None, "skipHash")
+ .map(|res| crate::config::tree::Index::SKIP_HASH.enrich_error(res))
+ .transpose()
+ .with_lenient_default(self.config.lenient_config)?
+ .unwrap_or_default();
+
+ let index = gix_index::File::at(
+ self.index_path(),
+ self.object_hash(),
+ skip_hash,
+ gix_index::decode::Options {
+ thread_limit,
+ min_extension_block_in_bytes_for_threading: 0,
+ expected_checksum: None,
+ },
+ )?;
+
+ Ok(index)
+ }
+
+ /// Return a shared worktree index which is updated automatically if the in-memory snapshot has become stale as the underlying file
+ /// on disk has changed.
+ ///
+ /// The index file is shared across all clones of this repository.
+ pub fn index(&self) -> Result<worktree::Index, worktree::open_index::Error> {
+ self.try_index().and_then(|opt| match opt {
+ Some(index) => Ok(index),
+ None => Err(worktree::open_index::Error::IndexFile(
+ gix_index::file::init::Error::Io(std::io::Error::new(
+ std::io::ErrorKind::NotFound,
+ format!("Could not find index file at {:?} for opening.", self.index_path()),
+ )),
+ )),
+ })
+ }
+
+ /// Return a shared worktree index which is updated automatically if the in-memory snapshot has become stale as the underlying file
+ /// on disk has changed, or `None` if no such file exists.
+ ///
+ /// The index file is shared across all clones of this repository.
+ pub fn try_index(&self) -> Result<Option<worktree::Index>, worktree::open_index::Error> {
+ self.index.recent_snapshot(
+ || self.index_path().metadata().and_then(|m| m.modified()).ok(),
+ || {
+ self.open_index().map(Some).or_else(|err| match err {
+ worktree::open_index::Error::IndexFile(gix_index::file::init::Error::Io(err))
+ if err.kind() == std::io::ErrorKind::NotFound =>
+ {
+ Ok(None)
+ }
+ err => Err(err),
+ })
+ },
+ )
+ }
+
+ /// Open the persisted worktree index or generate it from the current `HEAD^{tree}` to live in-memory only.
+ ///
+ /// Use this method to get an index in any repository, even bare ones that don't have one naturally.
+ ///
+ /// ### Note
+ ///
+ /// The locally stored index is not guaranteed to represent `HEAD^{tree}` if this repository is bare - bare repos
+ /// don't naturally have an index and if an index is present it must have been generated by hand.
+ pub fn index_or_load_from_head(
+ &self,
+ ) -> Result<IndexPersistedOrInMemory, crate::repository::index_or_load_from_head::Error> {
+ Ok(match self.try_index()? {
+ Some(index) => IndexPersistedOrInMemory::Persisted(index),
+ None => {
+ let tree = self.head_commit()?.tree_id()?;
+ IndexPersistedOrInMemory::InMemory(self.index_from_tree(&tree)?)
+ }
+ })
+ }
+
+ /// Create new index-file, which would live at the correct location, in memory from the given `tree`.
+ ///
+ /// Note that this is an expensive operation as it requires recursively traversing the entire tree to unpack it into the index.
+ pub fn index_from_tree(
+ &self,
+ tree: &gix_hash::oid,
+ ) -> Result<gix_index::File, gix_traverse::tree::breadthfirst::Error> {
+ Ok(gix_index::File::from_state(
+ gix_index::State::from_tree(tree, |oid, buf| self.objects.find_tree_iter(oid, buf).ok())?,
+ self.git_dir().join("index"),
+ ))
+ }
+}
+
+impl std::ops::Deref for IndexPersistedOrInMemory {
+ type Target = gix_index::File;
+
+ fn deref(&self) -> &Self::Target {
+ match self {
+ IndexPersistedOrInMemory::Persisted(i) => i,
+ IndexPersistedOrInMemory::InMemory(i) => i,
+ }
+ }
+}
+
+impl IndexPersistedOrInMemory {
+ /// Consume this instance and turn it into an owned index file.
+ ///
+ /// Note that this will cause the persisted index to be cloned, which would happen whenever the repository has a worktree.
+ pub fn into_owned(self) -> gix_index::File {
+ match self {
+ IndexPersistedOrInMemory::Persisted(i) => gix_index::File::clone(&i),
+ IndexPersistedOrInMemory::InMemory(i) => i,
+ }
+ }
+}
diff --git a/vendor/gix/src/repository/init.rs b/vendor/gix/src/repository/init.rs
index 255ff90d6..65b3d59ab 100644
--- a/vendor/gix/src/repository/init.rs
+++ b/vendor/gix/src/repository/init.rs
@@ -4,15 +4,16 @@ impl crate::Repository {
#[allow(clippy::too_many_arguments)]
pub(crate) fn from_refs_and_objects(
refs: crate::RefStore,
- objects: crate::OdbHandle,
+ mut objects: crate::OdbHandle,
work_tree: Option<std::path::PathBuf>,
common_dir: Option<std::path::PathBuf>,
config: crate::config::Cache,
linked_worktree_options: crate::open::Options,
- index: crate::worktree::IndexStorage,
+ #[cfg(feature = "index")] index: crate::worktree::IndexStorage,
shallow_commits: crate::shallow::CommitsStorage,
+ #[cfg(feature = "attributes")] modules: crate::submodule::ModulesFileStorage,
) -> Self {
- let objects = setup_objects(objects, &config);
+ setup_objects(&mut objects, &config);
crate::Repository {
bufs: RefCell::new(Vec::with_capacity(4)),
work_tree,
@@ -21,8 +22,11 @@ impl crate::Repository {
refs,
config,
options: linked_worktree_options,
+ #[cfg(feature = "index")]
index,
shallow_commits,
+ #[cfg(feature = "attributes")]
+ modules,
}
}
@@ -33,7 +37,7 @@ impl crate::Repository {
}
#[cfg_attr(not(feature = "max-performance-safe"), allow(unused_variables, unused_mut))]
-fn setup_objects(mut objects: crate::OdbHandle, config: &crate::config::Cache) -> crate::OdbHandle {
+pub(crate) fn setup_objects(objects: &mut crate::OdbHandle, config: &crate::config::Cache) {
#[cfg(feature = "max-performance-safe")]
{
match config.pack_cache_bytes {
@@ -54,10 +58,5 @@ fn setup_objects(mut objects: crate::OdbHandle, config: &crate::config::Cache) -
let bytes = config.object_cache_bytes;
objects.set_object_cache(move || Box::new(gix_pack::cache::object::MemoryCappedHashmap::new(bytes)));
}
- objects
- }
- #[cfg(not(feature = "max-performance-safe"))]
- {
- objects
}
}
diff --git a/vendor/gix/src/repository/location.rs b/vendor/gix/src/repository/location.rs
index 3e2ff907c..5811e7bf9 100644
--- a/vendor/gix/src/repository/location.rs
+++ b/vendor/gix/src/repository/location.rs
@@ -1,4 +1,4 @@
-use std::path::PathBuf;
+use std::path::{Path, PathBuf};
use gix_path::realpath::MAX_SYMLINKS;
@@ -25,6 +25,12 @@ impl crate::Repository {
self.git_dir().join("index")
}
+ /// The path to the `.gitmodules` file in the worktree, if a worktree is available.
+ #[cfg(feature = "attributes")]
+ pub fn modules_path(&self) -> Option<PathBuf> {
+ self.work_dir().map(|wtd| wtd.join(crate::submodule::MODULES_FILE))
+ }
+
/// The path to the `.git` directory itself, or equivalent if this is a bare repository.
pub fn path(&self) -> &std::path::Path {
self.git_dir()
@@ -42,30 +48,18 @@ impl crate::Repository {
}
/// Returns the relative path which is the components between the working tree and the current working dir (CWD).
- /// Note that there may be `None` if there is no work tree, even though the `PathBuf` will be empty
- /// if the CWD is at the root of the work tree.
+ /// Note that it may be `None` if there is no work tree, or if CWD isn't inside of the working tree directory.
+ ///
+ /// Note that the CWD is obtained once upon instantiation of the repository.
// TODO: tests, details - there is a lot about environment variables to change things around.
- pub fn prefix(&self) -> Option<std::io::Result<PathBuf>> {
- self.work_tree.as_ref().map(|root| {
- std::env::current_dir().and_then(|cwd| {
- gix_path::realpath_opts(root, &cwd, MAX_SYMLINKS)
- .map_err(|err| std::io::Error::new(std::io::ErrorKind::Other, err))
- .and_then(|root| {
- cwd.strip_prefix(&root)
- .map_err(|_| {
- std::io::Error::new(
- std::io::ErrorKind::Other,
- format!(
- "CWD '{}' isn't within the work tree '{}'",
- cwd.display(),
- root.display()
- ),
- )
- })
- .map(ToOwned::to_owned)
- })
- })
- })
+ pub fn prefix(&self) -> Result<Option<&Path>, gix_path::realpath::Error> {
+ let (root, current_dir) = match self.work_dir().zip(self.options.current_dir.as_deref()) {
+ Some((work_dir, cwd)) => (work_dir, cwd),
+ None => return Ok(None),
+ };
+
+ let root = gix_path::realpath_opts(root, current_dir, MAX_SYMLINKS)?;
+ Ok(current_dir.strip_prefix(&root).ok())
}
/// Return the kind of repository, either bare or one with a work tree.
diff --git a/vendor/gix/src/repository/snapshots.rs b/vendor/gix/src/repository/mailmap.rs
index 96de5080d..b4a2f4a0e 100644
--- a/vendor/gix/src/repository/snapshots.rs
+++ b/vendor/gix/src/repository/mailmap.rs
@@ -37,12 +37,11 @@ impl crate::Repository {
});
match self.work_dir() {
None => {
- // TODO: replace with ref-spec `HEAD:.mailmap` for less verbose way of getting the blob id
blob_id = blob_id.or_else(|| {
self.head().ok().and_then(|mut head| {
let commit = head.peel_to_commit_in_place().ok()?;
let tree = commit.tree().ok()?;
- tree.lookup_entry(Some(".mailmap")).ok()?.map(|e| e.object_id())
+ tree.find_entry(".mailmap").map(|e| e.object_id())
})
});
}
diff --git a/vendor/gix/src/repository/mod.rs b/vendor/gix/src/repository/mod.rs
index f8a51e8d0..e3742894b 100644
--- a/vendor/gix/src/repository/mod.rs
+++ b/vendor/gix/src/repository/mod.rs
@@ -4,6 +4,8 @@
#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub enum Kind {
/// A submodule worktree, whose `git` repository lives in `.git/modules/**/<name>` of the parent repository.
+ ///
+ /// Note that 'old-form' submodule will register as `Worktree {is_linked: false}`.
Submodule,
/// A bare repository does not have a work tree, that is files on disk beyond the `git` repository itself.
Bare,
@@ -33,22 +35,106 @@ impl crate::Repository {
}
}
-mod attributes;
+#[cfg(any(feature = "attributes", feature = "excludes"))]
+pub mod attributes;
mod cache;
mod config;
-mod excludes;
+///
+#[cfg(feature = "attributes")]
+pub mod filter;
mod graph;
pub(crate) mod identity;
mod impls;
-mod init;
+#[cfg(feature = "index")]
+mod index;
+pub(crate) mod init;
mod kind;
mod location;
+#[cfg(feature = "mailmap")]
+mod mailmap;
mod object;
+#[cfg(feature = "attributes")]
+mod pathspec;
mod reference;
mod remote;
+#[cfg(feature = "revision")]
mod revision;
mod shallow;
-mod snapshots;
mod state;
+#[cfg(feature = "attributes")]
+mod submodule;
mod thread_safe;
mod worktree;
+
+/// A type to represent an index which either was loaded from disk as it was persisted there, or created on the fly in memory.
+#[cfg(feature = "index")]
+pub enum IndexPersistedOrInMemory {
+ /// The index as loaded from disk, and shared across clones of the owning `Repository`.
+ Persisted(crate::worktree::Index),
+ /// A temporary index as created from the `HEAD^{tree}`, with the file path set to the place where it would be stored naturally.
+ ///
+ /// Note that unless saved explicitly, it will not persist.
+ InMemory(gix_index::File),
+}
+
+///
+#[cfg(feature = "attributes")]
+pub mod pathspec_defaults_ignore_case {
+ /// The error returned by [Repository::pathspec_defaults_ignore_case()](crate::Repository::pathspec_defaults_inherit_ignore_case()).
+ #[derive(Debug, thiserror::Error)]
+ #[allow(missing_docs)]
+ pub enum Error {
+ #[error("Filesystem configuration could not be obtained to learn about case sensitivity")]
+ FilesystemConfig(#[from] crate::config::boolean::Error),
+ #[error(transparent)]
+ Defaults(#[from] gix_pathspec::defaults::from_environment::Error),
+ }
+}
+
+///
+#[cfg(feature = "index")]
+pub mod index_or_load_from_head {
+ /// The error returned by [`Repository::index_or_load_from_head()`][crate::Repository::index_or_load_from_head()].
+ #[derive(thiserror::Error, Debug)]
+ #[allow(missing_docs)]
+ pub enum Error {
+ #[error(transparent)]
+ HeadCommit(#[from] crate::reference::head_commit::Error),
+ #[error(transparent)]
+ TreeId(#[from] gix_object::decode::Error),
+ #[error(transparent)]
+ TraverseTree(#[from] gix_traverse::tree::breadthfirst::Error),
+ #[error(transparent)]
+ OpenIndex(#[from] crate::worktree::open_index::Error),
+ }
+}
+
+///
+#[cfg(feature = "worktree-stream")]
+pub mod worktree_stream {
+ /// The error returned by [`Repository::worktree_stream()`][crate::Repository::worktree_stream()].
+ #[derive(Debug, thiserror::Error)]
+ #[allow(missing_docs)]
+ pub enum Error {
+ #[error(transparent)]
+ FindTree(#[from] crate::object::find::existing::Error),
+ #[error(transparent)]
+ OpenTree(#[from] gix_traverse::tree::breadthfirst::Error),
+ #[error(transparent)]
+ AttributesCache(#[from] crate::config::attribute_stack::Error),
+ #[error(transparent)]
+ FilterPipeline(#[from] crate::filter::pipeline::options::Error),
+ #[error("Needed {id} to be a tree to turn into a workspace stream, got {actual}")]
+ NotATree {
+ id: gix_hash::ObjectId,
+ actual: gix_object::Kind,
+ },
+ }
+}
+
+///
+#[cfg(feature = "worktree-archive")]
+pub mod worktree_archive {
+ /// The error returned by [`Repository::worktree_archive()`][crate::Repository::worktree_archive()].
+ pub type Error = gix_archive::Error;
+}
diff --git a/vendor/gix/src/repository/object.rs b/vendor/gix/src/repository/object.rs
index 787dcda4e..c156971d0 100644
--- a/vendor/gix/src/repository/object.rs
+++ b/vendor/gix/src/repository/object.rs
@@ -2,11 +2,13 @@
use std::{convert::TryInto, ops::DerefMut};
use gix_hash::ObjectId;
-use gix_odb::{Find, FindExt, Write};
+use gix_macros::momo;
+use gix_odb::{Find, FindExt, Header, HeaderExt, Write};
use gix_ref::{
transaction::{LogChange, PreviousValue, RefLog},
FullName,
};
+use smallvec::SmallVec;
use crate::{commit, ext::ObjectIdExt, object, tag, Id, Object, Reference, Tree};
@@ -21,6 +23,7 @@ impl crate::Repository {
///
/// In order to get the kind of the object, is must be fully decoded from storage if it is packed with deltas.
/// Loose object could be partially decoded, even though that's not implemented.
+ #[momo]
pub fn find_object(&self, id: impl Into<ObjectId>) -> Result<Object<'_>, object::find::existing::Error> {
let id = id.into();
if id == gix_hash::ObjectId::empty_tree(self.object_hash()) {
@@ -32,11 +35,46 @@ impl crate::Repository {
});
}
let mut buf = self.free_buf();
- let kind = self.objects.find(id, &mut buf)?.kind;
+ let kind = self.objects.find(&id, &mut buf)?.kind;
Ok(Object::from_data(id, kind, buf, self))
}
+ /// Obtain information about an object without fully decoding it, or fail if the object doesn't exist.
+ ///
+ /// Note that despite being cheaper than [`Self::find_object()`], there is still some effort traversing delta-chains.
+ #[doc(alias = "read_header", alias = "git2")]
+ #[momo]
+ pub fn find_header(&self, id: impl Into<ObjectId>) -> Result<gix_odb::find::Header, object::find::existing::Error> {
+ let id = id.into();
+ if id == gix_hash::ObjectId::empty_tree(self.object_hash()) {
+ return Ok(gix_odb::find::Header::Loose {
+ kind: gix_object::Kind::Tree,
+ size: 0,
+ });
+ }
+ self.objects.header(id)
+ }
+
+ /// Obtain information about an object without fully decoding it, or `None` if the object doesn't exist.
+ ///
+ /// Note that despite being cheaper than [`Self::try_find_object()`], there is still some effort traversing delta-chains.
+ #[momo]
+ pub fn try_find_header(
+ &self,
+ id: impl Into<ObjectId>,
+ ) -> Result<Option<gix_odb::find::Header>, object::find::Error> {
+ let id = id.into();
+ if id == gix_hash::ObjectId::empty_tree(self.object_hash()) {
+ return Ok(Some(gix_odb::find::Header::Loose {
+ kind: gix_object::Kind::Tree,
+ size: 0,
+ }));
+ }
+ self.objects.try_header(&id).map_err(Into::into)
+ }
+
/// Try to find the object with `id` or return `None` if it wasn't found.
+ #[momo]
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()) {
@@ -49,7 +87,7 @@ impl crate::Repository {
}
let mut buf = self.free_buf();
- match self.objects.try_find(id, &mut buf)? {
+ match self.objects.try_find(&id, &mut buf)? {
Some(obj) => {
let kind = obj.kind;
Ok(Some(Object::from_data(id, kind, buf, self)))
@@ -76,15 +114,19 @@ impl crate::Repository {
/// 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())?;
+ object.write_to(buf.deref_mut()).expect("write to memory works");
- let oid = gix_object::compute_hash(self.object_hash(), object.kind(), &buf);
- if self.objects.contains(oid) {
+ self.write_object_inner(&buf, object.kind())
+ }
+
+ fn write_object_inner(&self, buf: &[u8], kind: gix_object::Kind) -> Result<Id<'_>, object::write::Error> {
+ let oid = gix_object::compute_hash(self.object_hash(), kind, buf);
+ if self.objects.contains(&oid) {
return Ok(oid.attach(self));
}
self.objects
- .write_buf(object.kind(), &buf)
+ .write_buf(kind, buf)
.map(|oid| oid.attach(self))
.map_err(Into::into)
}
@@ -93,14 +135,16 @@ impl crate::Repository {
///
/// 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.
+ #[momo]
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) {
+ if self.objects.contains(&oid) {
return Ok(oid.attach(self));
}
self.objects
.write_buf(gix_object::Kind::Blob, bytes)
+ .map_err(Into::into)
.map(|oid| oid.attach(self))
}
@@ -115,14 +159,20 @@ impl crate::Repository {
mut bytes: impl std::io::Read + std::io::Seek,
) -> Result<Id<'_>, object::write::Error> {
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) {
+ std::io::copy(&mut bytes, buf.deref_mut()).expect("write to memory works");
+
+ self.write_blob_stream_inner(&buf)
+ }
+
+ fn write_blob_stream_inner(&self, buf: &[u8]) -> Result<Id<'_>, object::write::Error> {
+ 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_buf(gix_object::Kind::Blob, &buf)
+ .write_buf(gix_object::Kind::Blob, buf)
+ .map_err(Into::into)
.map(|oid| oid.attach(self))
}
@@ -131,6 +181,7 @@ impl crate::Repository {
///
/// It will be created with `constraint` which is most commonly to [only create it][PreviousValue::MustNotExist]
/// or to [force overwriting a possibly existing tag](PreviousValue::Any).
+ #[momo]
pub fn tag(
&self,
name: impl AsRef<str>,
@@ -168,6 +219,25 @@ impl crate::Repository {
Name: TryInto<FullName, Error = E>,
commit::Error: From<E>,
{
+ self.commit_as_inner(
+ committer.into(),
+ author.into(),
+ reference.try_into()?,
+ message.as_ref(),
+ tree.into(),
+ parents.into_iter().map(Into::into).collect(),
+ )
+ }
+
+ fn commit_as_inner(
+ &self,
+ committer: gix_actor::SignatureRef<'_>,
+ author: gix_actor::SignatureRef<'_>,
+ reference: FullName,
+ message: &str,
+ tree: ObjectId,
+ parents: SmallVec<[gix_hash::ObjectId; 1]>,
+ ) -> Result<Id<'_>, commit::Error> {
use gix_ref::{
transaction::{Change, RefEdit},
Target,
@@ -175,14 +245,13 @@ impl crate::Repository {
// TODO: possibly use CommitRef to save a few allocations (but will have to allocate for object ids anyway.
// This can be made vastly more efficient though if we wanted to, so we lie in the API
- let reference = reference.try_into()?;
let commit = gix_object::Commit {
- message: message.as_ref().into(),
- tree: tree.into(),
- author: author.into().to_owned(),
- committer: committer.into().to_owned(),
+ message: message.into(),
+ tree,
+ author: author.into(),
+ committer: committer.into(),
encoding: None,
- parents: parents.into_iter().map(|id| id.into()).collect(),
+ parents,
extra_headers: Default::default(),
};
diff --git a/vendor/gix/src/repository/pathspec.rs b/vendor/gix/src/repository/pathspec.rs
new file mode 100644
index 000000000..8e7e9bbe9
--- /dev/null
+++ b/vendor/gix/src/repository/pathspec.rs
@@ -0,0 +1,55 @@
+use gix_pathspec::MagicSignature;
+
+use crate::{bstr::BStr, config::cache::util::ApplyLeniencyDefault, AttributeStack, Pathspec, Repository};
+
+impl Repository {
+ /// Create a new pathspec abstraction that allows to conduct searches using `patterns`.
+ /// `inherit_ignore_case` should be `true` if `patterns` will match against files on disk, or `false` otherwise, for more natural matching
+ /// (but also note that `git` does not do that).
+ /// `index` may be needed to load attributes which is required only if `patterns` refer to attributes via `:(attr:…)` syntax.
+ /// In the same vein, `attributes_source` affects where `.gitattributes` files are read from if pathspecs need to match against attributes.
+ ///
+ /// It will be initialized exactly how it would, and attribute matching will be conducted by reading the worktree first if available.
+ /// If that is not desirable, consider calling [`Pathspec::new()`] directly.
+ #[doc(alias = "Pathspec", alias = "git2")]
+ pub fn pathspec(
+ &self,
+ patterns: impl IntoIterator<Item = impl AsRef<BStr>>,
+ inherit_ignore_case: bool,
+ index: &gix_index::State,
+ attributes_source: gix_worktree::stack::state::attributes::Source,
+ ) -> Result<Pathspec<'_>, crate::pathspec::init::Error> {
+ Pathspec::new(self, patterns, inherit_ignore_case, || {
+ self.attributes_only(index, attributes_source)
+ .map(AttributeStack::detach)
+ .map_err(Into::into)
+ })
+ }
+
+ /// Return default settings that are required when [parsing pathspecs](gix_pathspec::parse()) by hand.
+ ///
+ /// These are stemming from environment variables which have been converted to [config settings](crate::config::tree::gitoxide::Pathspec),
+ /// which now serve as authority for configuration.
+ pub fn pathspec_defaults(&self) -> Result<gix_pathspec::Defaults, gix_pathspec::defaults::from_environment::Error> {
+ self.config.pathspec_defaults()
+ }
+
+ /// Similar to [Self::pathspec_defaults()], but will automatically configure the returned defaults to match case-insensitively if the underlying
+ /// filesystem is also configured to be case-insensitive according to `core.ignoreCase`, and `inherit_ignore_case` is `true`.
+ pub fn pathspec_defaults_inherit_ignore_case(
+ &self,
+ inherit_ignore_case: bool,
+ ) -> Result<gix_pathspec::Defaults, crate::repository::pathspec_defaults_ignore_case::Error> {
+ let mut defaults = self.config.pathspec_defaults()?;
+ if inherit_ignore_case
+ && self
+ .config
+ .fs_capabilities()
+ .with_lenient_default(self.config.lenient_config)?
+ .ignore_case
+ {
+ defaults.signature |= MagicSignature::ICASE;
+ }
+ Ok(defaults)
+ }
+}
diff --git a/vendor/gix/src/repository/reference.rs b/vendor/gix/src/repository/reference.rs
index e5a8aadcb..5a14c60b5 100644
--- a/vendor/gix/src/repository/reference.rs
+++ b/vendor/gix/src/repository/reference.rs
@@ -1,6 +1,7 @@
use std::convert::TryInto;
use gix_hash::ObjectId;
+use gix_macros::momo;
use gix_ref::{
transaction::{Change, LogChange, PreviousValue, RefEdit, RefLog},
FullName, PartialNameRef, Target,
@@ -14,6 +15,7 @@ impl crate::Repository {
///
/// It will be created with `constraint` which is most commonly to [only create it][PreviousValue::MustNotExist]
/// or to [force overwriting a possibly existing tag](PreviousValue::Any).
+ #[momo]
pub fn tag_reference(
&self,
name: impl AsRef<str>,
@@ -60,10 +62,10 @@ impl crate::Repository {
pub fn set_namespace<'a, Name, E>(
&mut self,
namespace: Name,
- ) -> Result<Option<gix_ref::Namespace>, gix_validate::refname::Error>
+ ) -> Result<Option<gix_ref::Namespace>, gix_validate::reference::name::Error>
where
Name: TryInto<&'a PartialNameRef, Error = E>,
- gix_validate::refname::Error: From<E>,
+ gix_validate::reference::name::Error: From<E>,
{
let namespace = gix_ref::namespace::expand(namespace)?;
Ok(self.refs.namespace.replace(namespace))
@@ -85,14 +87,27 @@ impl crate::Repository {
Name: TryInto<FullName, Error = E>,
gix_validate::reference::name::Error: From<E>,
{
- let name = name.try_into().map_err(gix_validate::reference::name::Error::from)?;
- let id = target.into();
+ self.reference_inner(
+ name.try_into().map_err(gix_validate::reference::name::Error::from)?,
+ target.into(),
+ constraint,
+ log_message.into(),
+ )
+ }
+
+ fn reference_inner(
+ &self,
+ name: FullName,
+ id: ObjectId,
+ constraint: PreviousValue,
+ log_message: BString,
+ ) -> Result<Reference<'_>, reference::edit::Error> {
let mut edits = self.edit_reference(RefEdit {
change: Change::Update {
log: LogChange {
mode: RefLog::AndReference,
force_create_reflog: false,
- message: log_message.into(),
+ message: log_message,
},
expected: constraint,
new: Target::Peeled(id),
@@ -124,7 +139,7 @@ impl crate::Repository {
/// Edit one or more references as described by their `edits`.
/// Note that one can set the committer name for use in the ref-log by temporarily
- /// [overriding the gix-config][crate::Repository::config_snapshot_mut()].
+ /// [overriding the git-config][crate::Repository::config_snapshot_mut()].
///
/// Returns all reference edits, which might be more than where provided due the splitting of symbolic references, and
/// whose previous (_old_) values are the ones seen on in storage after the reference was locked.
@@ -180,7 +195,7 @@ impl crate::Repository {
/// The difference to [`head_ref()`][Self::head_ref()] is that the latter requires the reference to exist,
/// whereas here we merely return a the name of the possibly unborn reference.
pub fn head_name(&self) -> Result<Option<FullName>, reference::find::existing::Error> {
- Ok(self.head()?.referent_name().map(|n| n.to_owned()))
+ Ok(self.head()?.referent_name().map(std::borrow::ToOwned::to_owned))
}
/// Return the reference that `HEAD` points to, or `None` if the head is detached or unborn.
diff --git a/vendor/gix/src/repository/remote.rs b/vendor/gix/src/repository/remote.rs
index 74ebbaea0..be0845178 100644
--- a/vendor/gix/src/repository/remote.rs
+++ b/vendor/gix/src/repository/remote.rs
@@ -28,7 +28,8 @@ impl crate::Repository {
Remote::from_fetch_url(url, false, self)
}
- /// Find the remote with the given `name_or_url` or report an error, similar to [`try_find_remote(…)`][Self::try_find_remote()].
+ /// Find the configured remote with the given `name_or_url` or report an error,
+ /// similar to [`try_find_remote(…)`][Self::try_find_remote()].
///
/// Note that we will obtain remotes only if we deem them [trustworthy][crate::open::Options::filter_config_section()].
pub fn find_remote<'a>(&self, name_or_url: impl Into<&'a BStr>) -> Result<Remote<'_>, find::existing::Error> {
@@ -42,7 +43,7 @@ impl crate::Repository {
/// Find the default remote as configured, or `None` if no such configuration could be found.
///
- /// See [`remote_default_name()`][Self::remote_default_name()] for more information on the `direction` parameter.
+ /// See [`remote_default_name()`](Self::remote_default_name()) for more information on the `direction` parameter.
pub fn find_default_remote(
&self,
direction: remote::Direction,
@@ -51,8 +52,8 @@ impl crate::Repository {
.map(|name| self.find_remote(name.as_ref()))
}
- /// Find the remote with the given `name_or_url` or return `None` if it doesn't exist, for the purpose of fetching or pushing
- /// data to a remote.
+ /// Find the configured remote with the given `name_or_url` or return `None` if it doesn't exist,
+ /// for the purpose of fetching or pushing data.
///
/// There are various error kinds related to partial information or incorrectly formatted URLs or ref-specs.
/// Also note that the created `Remote` may have neither fetch nor push ref-specs set at all.
@@ -62,7 +63,36 @@ impl crate::Repository {
///
/// We will only include information if we deem it [trustworthy][crate::open::Options::filter_config_section()].
pub fn try_find_remote<'a>(&self, name_or_url: impl Into<&'a BStr>) -> Option<Result<Remote<'_>, find::Error>> {
- self.try_find_remote_inner(name_or_url, true)
+ self.try_find_remote_inner(name_or_url.into(), true)
+ }
+
+ /// This method emulate what `git fetch <remote>` does in order to obtain a remote to fetch from.
+ ///
+ /// As such, with `name_or_url` being `Some`, it will:
+ ///
+ /// * use `name_or_url` verbatim if it is a URL, creating a new remote in memory as needed.
+ /// * find the named remote if `name_or_url` is a remote name
+ ///
+ /// If `name_or_url` is `None`:
+ ///
+ /// * use the current `HEAD` branch to find a configured remote
+ /// * fall back to either a generally configured remote or the only configured remote.
+ ///
+ /// Fail if no remote could be found despite all of the above.
+ pub fn find_fetch_remote(&self, name_or_url: Option<&BStr>) -> Result<Remote<'_>, find::for_fetch::Error> {
+ Ok(match name_or_url {
+ Some(name) => match self.try_find_remote(name).and_then(Result::ok) {
+ Some(remote) => remote,
+ None => self.remote_at(gix_url::parse(name)?)?,
+ },
+ None => self
+ .head()?
+ .into_remote(remote::Direction::Fetch)
+ .transpose()?
+ .map(Ok)
+ .or_else(|| self.find_default_remote(remote::Direction::Fetch))
+ .ok_or_else(|| find::for_fetch::Error::ExactlyOneRemoteNotAvailable)??,
+ })
}
/// Similar to [`try_find_remote()`][Self::try_find_remote()], but removes a failure mode if rewritten URLs turn out to be invalid
@@ -72,7 +102,7 @@ impl crate::Repository {
&self,
name_or_url: impl Into<&'a BStr>,
) -> Option<Result<Remote<'_>, find::Error>> {
- self.try_find_remote_inner(name_or_url, false)
+ self.try_find_remote_inner(name_or_url.into(), false)
}
fn try_find_remote_inner<'a>(
diff --git a/vendor/gix/src/repository/revision.rs b/vendor/gix/src/repository/revision.rs
index 3018c2be8..bb9b56d57 100644
--- a/vendor/gix/src/repository/revision.rs
+++ b/vendor/gix/src/repository/revision.rs
@@ -1,4 +1,5 @@
use crate::{bstr::BStr, revision, Id};
+use gix_macros::momo;
/// Methods for resolving revisions by spec or working with the commit graph.
impl crate::Repository {
@@ -8,6 +9,8 @@ impl crate::Repository {
///
/// - `@` actually stands for `HEAD`, whereas `git` resolves it to the object pointed to by `HEAD` without making the
/// `HEAD` ref available for lookups.
+ #[doc(alias = "revparse", alias = "git2")]
+ #[momo]
pub fn rev_parse<'a>(&self, spec: impl Into<&'a BStr>) -> Result<revision::Spec<'_>, revision::spec::parse::Error> {
revision::Spec::from_bstr(
spec,
@@ -20,6 +23,7 @@ impl crate::Repository {
}
/// Parse a revision specification and return single object id as represented by this instance.
+ #[doc(alias = "revparse_single", alias = "git2")]
pub fn rev_parse_single<'repo, 'a>(
&'repo self,
spec: impl Into<&'a BStr>,
@@ -33,6 +37,7 @@ impl crate::Repository {
/// Create the baseline for a revision walk by initializing it with the `tips` to start iterating on.
///
/// It can be configured further before starting the actual walk.
+ #[doc(alias = "revwalk", alias = "git2")]
pub fn rev_walk(
&self,
tips: impl IntoIterator<Item = impl Into<gix_hash::ObjectId>>,
diff --git a/vendor/gix/src/repository/submodule.rs b/vendor/gix/src/repository/submodule.rs
new file mode 100644
index 000000000..a605bfbd3
--- /dev/null
+++ b/vendor/gix/src/repository/submodule.rs
@@ -0,0 +1,96 @@
+use std::rc::Rc;
+
+use crate::{submodule, Repository};
+
+impl Repository {
+ /// Open the `.gitmodules` file as present in the worktree, or return `None` if no such file is available.
+ /// Note that git configuration is also contributing to the result based on the current snapshot.
+ ///
+ /// Note that his method will not look in other places, like the index or the `HEAD` tree.
+ // TODO(submodule): make it use an updated snapshot instead once we have `config()`.
+ pub fn open_modules_file(&self) -> Result<Option<gix_submodule::File>, submodule::open_modules_file::Error> {
+ let path = match self.modules_path() {
+ Some(path) => path,
+ None => return Ok(None),
+ };
+ let buf = match std::fs::read(&path) {
+ Ok(buf) => buf,
+ Err(err) if err.kind() == std::io::ErrorKind::NotFound => return Ok(None),
+ Err(err) => return Err(err.into()),
+ };
+
+ Ok(Some(gix_submodule::File::from_bytes(
+ &buf,
+ path,
+ &self.config.resolved,
+ )?))
+ }
+
+ /// Return a shared [`.gitmodules` file](crate::submodule::File) which is updated automatically if the in-memory snapshot
+ /// has become stale as the underlying file on disk has changed. The snapshot based on the file on disk is shared across all
+ /// clones of this repository.
+ ///
+ /// If a file on disk isn't present, we will try to load it from the index, and finally from the current tree.
+ /// In the latter two cases, the result will not be cached in this repository instance as we can't detect freshness anymore,
+ /// so time this method is called a new [modules file](submodule::ModulesSnapshot) will be created.
+ ///
+ /// Note that git configuration is also contributing to the result based on the current snapshot.
+ ///
+ // TODO(submodule): make it use an updated snapshot instead once we have `config()`.
+ pub fn modules(&self) -> Result<Option<submodule::ModulesSnapshot>, submodule::modules::Error> {
+ match self.modules.recent_snapshot(
+ || {
+ self.modules_path()
+ .and_then(|path| path.metadata().and_then(|m| m.modified()).ok())
+ },
+ || self.open_modules_file(),
+ )? {
+ Some(m) => Ok(Some(m)),
+ None => {
+ let id = match self.try_index()?.and_then(|index| {
+ index
+ .entry_by_path(submodule::MODULES_FILE.into())
+ .map(|entry| entry.id)
+ }) {
+ Some(id) => id,
+ None => match self
+ .head_commit()?
+ .tree()?
+ .find_entry(submodule::MODULES_FILE)
+ .map(|entry| entry.inner.oid)
+ {
+ Some(id) => id.to_owned(),
+ None => return Ok(None),
+ },
+ };
+ Ok(Some(gix_features::threading::OwnShared::new(
+ gix_submodule::File::from_bytes(&self.find_object(id)?.data, None, &self.config.resolved)
+ .map_err(submodule::open_modules_file::Error::from)?
+ .into(),
+ )))
+ }
+ }
+ }
+
+ /// Return the list of available submodules, or `None` if there is no submodule configuration.
+ #[doc(alias = "git2")]
+ pub fn submodules(&self) -> Result<Option<impl Iterator<Item = crate::Submodule<'_>>>, submodule::modules::Error> {
+ let modules = match self.modules()? {
+ None => return Ok(None),
+ Some(m) => m,
+ };
+ let shared_state = Rc::new(submodule::SharedState::new(self, modules));
+ Ok(Some(
+ shared_state
+ .modules
+ .names()
+ .map(ToOwned::to_owned)
+ .collect::<Vec<_>>()
+ .into_iter()
+ .map(move |name| crate::Submodule {
+ state: shared_state.clone(),
+ name,
+ }),
+ ))
+ }
+}
diff --git a/vendor/gix/src/repository/worktree.rs b/vendor/gix/src/repository/worktree.rs
index f522a3f18..cc6f0bf73 100644
--- a/vendor/gix/src/repository/worktree.rs
+++ b/vendor/gix/src/repository/worktree.rs
@@ -1,4 +1,4 @@
-use crate::{config::cache::util::ApplyLeniencyDefault, worktree, Worktree};
+use crate::{worktree, Worktree};
/// Interact with individual worktrees and their information.
impl crate::Repository {
@@ -37,7 +37,8 @@ impl crate::Repository {
/// Return the currently set worktree if there is one, acting as platform providing a validated worktree base path.
///
/// Note that there would be `None` if this repository is `bare` and the parent [`Repository`][crate::Repository] was instantiated without
- /// registered worktree in the current working dir.
+ /// registered worktree in the current working dir, even if no `.git` file or directory exists.
+ /// It's merely based on configuration, see [Worktree::dot_git_exists()] for a way to perform more validation.
pub fn worktree(&self) -> Option<Worktree<'_>> {
self.work_dir().map(|path| Worktree { parent: self, path })
}
@@ -50,57 +51,95 @@ impl crate::Repository {
self.config.is_bare && self.work_dir().is_none()
}
- /// Open a new copy of the index file and decode it entirely.
+ /// If `id` points to a tree, produce a stream that yields one worktree entry after the other. The index of the tree at `id`
+ /// is returned as well as it is an intermediate byproduct that might be useful to callers.
///
- /// 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.
- pub fn open_index(&self) -> Result<gix_index::File, worktree::open_index::Error> {
- let thread_limit = self
- .config
- .resolved
- .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(),
- gix_index::decode::Options {
- thread_limit,
- min_extension_block_in_bytes_for_threading: 0,
- expected_checksum: None,
+ /// The entries will look exactly like they would if one would check them out, with filters applied.
+ /// The `export-ignore` attribute is used to skip blobs or directories to which it applies.
+ #[cfg(feature = "worktree-stream")]
+ #[gix_macros::momo]
+ pub fn worktree_stream(
+ &self,
+ id: impl Into<gix_hash::ObjectId>,
+ ) -> Result<(gix_worktree_stream::Stream, gix_index::File), crate::repository::worktree_stream::Error> {
+ use gix_odb::{FindExt, HeaderExt};
+ let id = id.into();
+ let header = self.objects.header(id)?;
+ if !header.kind().is_tree() {
+ return Err(crate::repository::worktree_stream::Error::NotATree {
+ id,
+ actual: header.kind(),
+ });
+ }
+
+ // TODO(perf): potential performance improvements could be to use the index at `HEAD` if possible (`index_from_head_tree…()`)
+ // TODO(perf): when loading a non-HEAD tree, we effectively traverse the tree twice. This is usually fast though, and sharing
+ // an object cache between the copies of the ODB handles isn't trivial and needs a lock.
+ let index = self.index_from_tree(&id)?;
+ let mut cache = self
+ .attributes_only(&index, gix_worktree::stack::state::attributes::Source::IdMapping)?
+ .detach();
+ let pipeline =
+ gix_filter::Pipeline::new(cache.attributes_collection(), crate::filter::Pipeline::options(self)?);
+ let objects = self.objects.clone().into_arc().expect("TBD error handling");
+ let stream = gix_worktree_stream::from_tree(
+ id,
+ {
+ let objects = objects.clone();
+ move |id, buf| objects.find(id, buf)
+ },
+ pipeline,
+ move |path, mode, attrs| -> std::io::Result<()> {
+ let entry = cache.at_entry(path, Some(mode.is_tree()), |id, buf| objects.find_blob(id, buf))?;
+ entry.matching_attributes(attrs);
+ Ok(())
},
- )
- .map_err(Into::into)
+ );
+ Ok((stream, index))
}
- /// Return a shared worktree index which is updated automatically if the in-memory snapshot has become stale as the underlying file
- /// on disk has changed.
+ /// Produce an archive from the `stream` and write it to `out` according to `options`.
+ /// Use `blob` to provide progress for each entry written to `out`, and note that it should already be initialized to the amount
+ /// of expected entries, with `should_interrupt` being queried between each entry to abort if needed, and on each write to `out`.
+ ///
+ /// ### Performance
+ ///
+ /// Be sure that `out` is able to handle a lot of write calls. Otherwise wrap it in a [`BufWriter`][std::io::BufWriter].
///
- /// The index file is shared across all clones of this repository.
- pub fn index(&self) -> Result<worktree::Index, worktree::open_index::Error> {
- self.index
- .recent_snapshot(
- || self.index_path().metadata().and_then(|m| m.modified()).ok(),
- || {
- self.open_index().map(Some).or_else(|err| match err {
- worktree::open_index::Error::IndexFile(gix_index::file::init::Error::Io(err))
- if err.kind() == std::io::ErrorKind::NotFound =>
- {
- Ok(None)
- }
- err => Err(err),
- })
- },
- )
- .and_then(|opt| match opt {
- Some(index) => Ok(index),
- None => Err(worktree::open_index::Error::IndexFile(
- gix_index::file::init::Error::Io(std::io::Error::new(
- std::io::ErrorKind::NotFound,
- format!("Could not find index file at {:?} for opening.", self.index_path()),
- )),
- )),
- })
+ /// ### Additional progress and fine-grained interrupt handling
+ ///
+ /// For additional progress reporting, wrap `out` into a writer that counts throughput on each write.
+ /// This can also be used to react to interrupts on each write, instead of only for each entry.
+ #[cfg(feature = "worktree-archive")]
+ pub fn worktree_archive(
+ &self,
+ mut stream: gix_worktree_stream::Stream,
+ out: impl std::io::Write + std::io::Seek,
+ blobs: impl gix_features::progress::Count,
+ should_interrupt: &std::sync::atomic::AtomicBool,
+ options: gix_archive::Options,
+ ) -> Result<(), crate::repository::worktree_archive::Error> {
+ let mut out = gix_features::interrupt::Write {
+ inner: out,
+ should_interrupt,
+ };
+ if options.format == gix_archive::Format::InternalTransientNonPersistable {
+ std::io::copy(&mut stream.into_read(), &mut out)?;
+ return Ok(());
+ }
+ gix_archive::write_stream_seek(
+ &mut stream,
+ |stream| {
+ if should_interrupt.load(std::sync::atomic::Ordering::Relaxed) {
+ return Err(std::io::Error::new(std::io::ErrorKind::Other, "Cancelled by user").into());
+ }
+ let res = stream.next_entry();
+ blobs.inc();
+ res
+ },
+ out,
+ options,
+ )?;
+ Ok(())
}
}