summaryrefslogtreecommitdiffstats
path: root/vendor/gix/src
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/gix/src')
-rw-r--r--vendor/gix/src/attribute_stack.rs12
-rw-r--r--vendor/gix/src/clone/checkout.rs20
-rw-r--r--vendor/gix/src/clone/fetch/util.rs3
-rw-r--r--vendor/gix/src/commit.rs8
-rw-r--r--vendor/gix/src/config/cache/access.rs121
-rw-r--r--vendor/gix/src/config/cache/init.rs68
-rw-r--r--vendor/gix/src/config/cache/util.rs12
-rw-r--r--vendor/gix/src/config/mod.rs88
-rw-r--r--vendor/gix/src/config/snapshot/access.rs13
-rw-r--r--vendor/gix/src/config/snapshot/credential_helpers.rs24
-rw-r--r--vendor/gix/src/config/tree/sections/core.rs6
-rw-r--r--vendor/gix/src/config/tree/sections/diff.rs74
-rw-r--r--vendor/gix/src/config/tree/sections/fetch.rs3
-rw-r--r--vendor/gix/src/config/tree/sections/gitoxide.rs90
-rw-r--r--vendor/gix/src/config/tree/sections/http.rs4
-rw-r--r--vendor/gix/src/create.rs2
-rw-r--r--vendor/gix/src/diff.rs127
-rw-r--r--vendor/gix/src/ext/object_id.rs10
-rw-r--r--vendor/gix/src/ext/rev_spec.rs1
-rw-r--r--vendor/gix/src/ext/tree.rs11
-rw-r--r--vendor/gix/src/filter.rs16
-rw-r--r--vendor/gix/src/head/peel.rs158
-rw-r--r--vendor/gix/src/lib.rs6
-rw-r--r--vendor/gix/src/object/blob.rs276
-rw-r--r--vendor/gix/src/object/errors.rs6
-rw-r--r--vendor/gix/src/object/tree/diff/change.rs47
-rw-r--r--vendor/gix/src/object/tree/diff/for_each.rs155
-rw-r--r--vendor/gix/src/object/tree/diff/mod.rs32
-rw-r--r--vendor/gix/src/object/tree/diff/rewrites.rs108
-rw-r--r--vendor/gix/src/object/tree/diff/tracked.rs490
-rw-r--r--vendor/gix/src/object/tree/iter.rs2
-rw-r--r--vendor/gix/src/object/tree/mod.rs5
-rw-r--r--vendor/gix/src/object/tree/traverse.rs9
-rw-r--r--vendor/gix/src/open/repository.rs31
-rw-r--r--vendor/gix/src/pathspec.rs7
-rw-r--r--vendor/gix/src/prelude.rs3
-rw-r--r--vendor/gix/src/reference/edits.rs3
-rw-r--r--vendor/gix/src/reference/errors.rs8
-rw-r--r--vendor/gix/src/reference/iter.rs14
-rw-r--r--vendor/gix/src/reference/mod.rs10
-rw-r--r--vendor/gix/src/remote/connect.rs6
-rw-r--r--vendor/gix/src/remote/connection/fetch/mod.rs3
-rw-r--r--vendor/gix/src/remote/connection/fetch/negotiate.rs2
-rw-r--r--vendor/gix/src/remote/connection/fetch/receive_pack.rs59
-rw-r--r--vendor/gix/src/remote/connection/fetch/update_refs/mod.rs14
-rw-r--r--vendor/gix/src/remote/connection/fetch/update_refs/tests.rs2
-rw-r--r--vendor/gix/src/remote/connection/mod.rs1
-rw-r--r--vendor/gix/src/remote/connection/ref_map.rs5
-rw-r--r--vendor/gix/src/repository/config/mod.rs46
-rw-r--r--vendor/gix/src/repository/config/transport.rs24
-rw-r--r--vendor/gix/src/repository/diff.rs45
-rw-r--r--vendor/gix/src/repository/filter.rs4
-rw-r--r--vendor/gix/src/repository/graph.rs8
-rw-r--r--vendor/gix/src/repository/index.rs4
-rw-r--r--vendor/gix/src/repository/kind.rs2
-rw-r--r--vendor/gix/src/repository/mod.rs5
-rw-r--r--vendor/gix/src/repository/object.rs11
-rw-r--r--vendor/gix/src/repository/reference.rs18
-rw-r--r--vendor/gix/src/repository/revision.rs3
-rw-r--r--vendor/gix/src/repository/worktree.rs12
-rw-r--r--vendor/gix/src/revision/mod.rs3
-rw-r--r--vendor/gix/src/revision/spec/mod.rs9
-rw-r--r--vendor/gix/src/revision/spec/parse/delegate/mod.rs2
-rw-r--r--vendor/gix/src/revision/spec/parse/delegate/navigate.rs20
-rw-r--r--vendor/gix/src/revision/spec/parse/mod.rs4
-rw-r--r--vendor/gix/src/revision/walk.rs4
-rw-r--r--vendor/gix/src/submodule/mod.rs7
67 files changed, 1339 insertions, 1067 deletions
diff --git a/vendor/gix/src/attribute_stack.rs b/vendor/gix/src/attribute_stack.rs
index 1aaca0f2b..66497def6 100644
--- a/vendor/gix/src/attribute_stack.rs
+++ b/vendor/gix/src/attribute_stack.rs
@@ -1,9 +1,7 @@
-use crate::bstr::BStr;
-use crate::types::AttributeStack;
-use crate::Repository;
-use gix_odb::FindExt;
use std::ops::{Deref, DerefMut};
+use crate::{bstr::BStr, types::AttributeStack, Repository};
+
/// Lifecycle
impl<'repo> AttributeStack<'repo> {
/// Create a new instance from a `repo` and the underlying pre-configured `stack`.
@@ -46,8 +44,7 @@ impl<'repo> AttributeStack<'repo> {
relative: impl AsRef<std::path::Path>,
is_dir: Option<bool>,
) -> std::io::Result<gix_worktree::stack::Platform<'_>> {
- self.inner
- .at_path(relative, is_dir, |id, buf| self.repo.objects.find_blob(id, buf))
+ self.inner.at_path(relative, is_dir, &self.repo.objects)
}
/// Obtain a platform for lookups from a repo-`relative` path, typically obtained from an index entry. `is_dir` should reflect
@@ -63,7 +60,6 @@ impl<'repo> AttributeStack<'repo> {
relative: impl Into<&'r BStr>,
is_dir: Option<bool>,
) -> std::io::Result<gix_worktree::stack::Platform<'_>> {
- self.inner
- .at_entry(relative, is_dir, |id, buf| self.repo.objects.find_blob(id, buf))
+ self.inner.at_entry(relative, is_dir, &self.repo.objects)
}
}
diff --git a/vendor/gix/src/clone/checkout.rs b/vendor/gix/src/clone/checkout.rs
index ece480a56..84a3fedbd 100644
--- a/vendor/gix/src/clone/checkout.rs
+++ b/vendor/gix/src/clone/checkout.rs
@@ -4,8 +4,6 @@ use crate::{clone::PrepareCheckout, Repository};
pub mod main_worktree {
use std::{path::PathBuf, sync::atomic::AtomicBool};
- use gix_odb::FindExt;
-
use crate::{clone::PrepareCheckout, Progress, Repository};
/// The error returned by [`PrepareCheckout::main_worktree()`].
@@ -26,7 +24,7 @@ pub mod main_worktree {
#[error(transparent)]
CheckoutOptions(#[from] crate::config::checkout_options::Error),
#[error(transparent)]
- IndexCheckout(#[from] gix_worktree_state::checkout::Error<gix_odb::find::existing_object::Error>),
+ IndexCheckout(#[from] gix_worktree_state::checkout::Error),
#[error("Failed to reopen object database as Arc (only if thread-safety wasn't compiled in)")]
OpenArcOdb(#[from] std::io::Error),
#[error("The HEAD reference could not be located")]
@@ -87,7 +85,7 @@ pub mod main_worktree {
let workdir = repo.work_dir().ok_or_else(|| Error::BareRepository {
git_dir: repo.git_dir().to_owned(),
})?;
- let root_tree = match repo.head()?.peel_to_id_in_place().transpose()? {
+ let root_tree = match repo.head()?.try_peel_to_id_in_place()? {
Some(id) => id.object().expect("downloaded from remote").peel_to_tree()?.id,
None => {
return Ok((
@@ -96,11 +94,10 @@ pub mod main_worktree {
))
}
};
- let index = gix_index::State::from_tree(&root_tree, |oid, buf| repo.objects.find_tree_iter(oid, buf).ok())
- .map_err(|err| Error::IndexFromTree {
- id: root_tree,
- source: err,
- })?;
+ let index = gix_index::State::from_tree(&root_tree, &repo.objects).map_err(|err| Error::IndexFromTree {
+ id: root_tree,
+ source: err,
+ })?;
let mut index = gix_index::File::from_state(index, repo.index_path());
let mut opts = repo
@@ -118,10 +115,7 @@ pub mod main_worktree {
let outcome = gix_worktree_state::checkout(
&mut index,
workdir,
- {
- let objects = repo.objects.clone().into_arc()?;
- move |oid, buf| objects.find_blob(oid, buf)
- },
+ repo.objects.clone().into_arc()?,
&files,
&bytes,
should_interrupt,
diff --git a/vendor/gix/src/clone/fetch/util.rs b/vendor/gix/src/clone/fetch/util.rs
index db9bc0a1c..627201301 100644
--- a/vendor/gix/src/clone/fetch/util.rs
+++ b/vendor/gix/src/clone/fetch/util.rs
@@ -1,6 +1,5 @@
use std::{borrow::Cow, convert::TryInto, io::Write};
-use gix_odb::Find;
use gix_ref::{
transaction::{LogChange, RefLog},
FullNameRef,
@@ -107,7 +106,7 @@ pub fn update_head(
repo.refs
.transaction()
.packed_refs(gix_ref::file::transaction::PackedRefs::DeletionsAndNonSymbolicUpdates(
- Box::new(|oid, buf| repo.objects.try_find(&oid, buf).map(|obj| obj.map(|obj| obj.kind))),
+ Box::new(&repo.objects),
))
.prepare(
{
diff --git a/vendor/gix/src/commit.rs b/vendor/gix/src/commit.rs
index 2cc8226f5..ce5dee4d6 100644
--- a/vendor/gix/src/commit.rs
+++ b/vendor/gix/src/commit.rs
@@ -28,7 +28,6 @@ pub mod describe {
use gix_hash::ObjectId;
use gix_hashtable::HashMap;
- use gix_odb::Find;
use crate::{bstr::BStr, ext::ObjectIdExt, Repository};
@@ -199,12 +198,7 @@ pub mod describe {
pub fn try_resolve(&self) -> Result<Option<Resolution<'repo>>, Error> {
// TODO: dirty suffix with respective dirty-detection
let mut graph = gix_revwalk::Graph::new(
- |id, buf| {
- self.repo
- .objects
- .try_find(id, buf)
- .map(|r| r.and_then(gix_object::Data::try_into_commit_iter))
- },
+ &self.repo.objects,
gix_commitgraph::Graph::from_info_dir(self.repo.objects.store_ref().path().join("info").as_ref()).ok(),
);
let outcome = gix_revision::describe(
diff --git a/vendor/gix/src/config/cache/access.rs b/vendor/gix/src/config/cache/access.rs
index 3e763c028..464a0bf4d 100644
--- a/vendor/gix/src/config/cache/access.rs
+++ b/vendor/gix/src/config/cache/access.rs
@@ -20,8 +20,7 @@ use crate::{
impl Cache {
#[cfg(feature = "blob-diff")]
pub(crate) fn diff_algorithm(&self) -> Result<gix_diff::blob::Algorithm, config::diff::algorithm::Error> {
- use crate::config::cache::util::ApplyLeniencyDefault;
- use crate::config::diff::algorithm::Error;
+ use crate::config::{cache::util::ApplyLeniencyDefault, diff::algorithm::Error};
self.diff_algorithm
.get_or_try_init(|| {
let name = self
@@ -39,6 +38,97 @@ impl Cache {
.copied()
}
+ #[cfg(feature = "blob-diff")]
+ pub(crate) fn diff_drivers(&self) -> Result<Vec<gix_diff::blob::Driver>, config::diff::drivers::Error> {
+ use crate::config::cache::util::ApplyLeniencyDefault;
+ let mut out = Vec::<gix_diff::blob::Driver>::new();
+ for section in self
+ .resolved
+ .sections_by_name("diff")
+ .into_iter()
+ .flatten()
+ .filter(|s| (self.filter_config_section)(s.meta()))
+ {
+ let Some(name) = section.header().subsection_name().filter(|n| !n.is_empty()) else {
+ continue;
+ };
+
+ let driver = match out.iter_mut().find(|d| d.name == name) {
+ Some(existing) => existing,
+ None => {
+ out.push(gix_diff::blob::Driver {
+ name: name.into(),
+ ..Default::default()
+ });
+ out.last_mut().expect("just pushed")
+ }
+ };
+
+ if let Some(binary) = section.value_implicit("binary") {
+ driver.is_binary = config::tree::Diff::DRIVER_BINARY
+ .try_into_binary(binary)
+ .with_leniency(self.lenient_config)
+ .map_err(|err| config::diff::drivers::Error {
+ name: driver.name.clone(),
+ attribute: "binary",
+ source: Box::new(err),
+ })?;
+ }
+ if let Some(command) = section.value(config::tree::Diff::DRIVER_COMMAND.name) {
+ driver.command = command.into_owned().into();
+ }
+ if let Some(textconv) = section.value(config::tree::Diff::DRIVER_TEXTCONV.name) {
+ driver.binary_to_text_command = textconv.into_owned().into();
+ }
+ if let Some(algorithm) = section.value("algorithm") {
+ driver.algorithm = config::tree::Diff::DRIVER_ALGORITHM
+ .try_into_algorithm(algorithm)
+ .or_else(|err| match err {
+ config::diff::algorithm::Error::Unimplemented { .. } if self.lenient_config => {
+ Ok(gix_diff::blob::Algorithm::Histogram)
+ }
+ err => Err(err),
+ })
+ .with_lenient_default(self.lenient_config)
+ .map_err(|err| config::diff::drivers::Error {
+ name: driver.name.clone(),
+ attribute: "algorithm",
+ source: Box::new(err),
+ })?
+ .into();
+ }
+ }
+ Ok(out)
+ }
+
+ #[cfg(feature = "blob-diff")]
+ pub(crate) fn diff_pipeline_options(
+ &self,
+ ) -> Result<gix_diff::blob::pipeline::Options, config::diff::pipeline_options::Error> {
+ Ok(gix_diff::blob::pipeline::Options {
+ large_file_threshold_bytes: self.big_file_threshold()?,
+ fs: self.fs_capabilities()?,
+ })
+ }
+
+ #[cfg(feature = "blob-diff")]
+ pub(crate) fn diff_renames(&self) -> Result<Option<crate::diff::Rewrites>, crate::diff::new_rewrites::Error> {
+ self.diff_renames
+ .get_or_try_init(|| crate::diff::new_rewrites(&self.resolved, self.lenient_config))
+ .copied()
+ }
+
+ #[cfg(feature = "blob-diff")]
+ pub(crate) fn big_file_threshold(&self) -> Result<u64, config::unsigned_integer::Error> {
+ Ok(self
+ .resolved
+ .integer_by_key("core.bigFileThreshold")
+ .map(|number| Core::BIG_FILE_THRESHOLD.try_into_u64(number))
+ .transpose()
+ .with_leniency(self.lenient_config)?
+ .unwrap_or(512 * 1024 * 1024))
+ }
+
/// Returns a user agent for use with servers.
#[cfg(any(feature = "async-network-client", feature = "blocking-network-client"))]
pub(crate) fn user_agent_tuple(&self) -> (&'static str, Option<Cow<'static, str>>) {
@@ -54,6 +144,18 @@ impl Cache {
("agent", Some(gix_protocol::agent(agent).into()))
}
+ /// Return `true` if packet-tracing is enabled. Lenient and defaults to `false`.
+ #[cfg(any(feature = "async-network-client", feature = "blocking-network-client"))]
+ pub(crate) fn trace_packet(&self) -> bool {
+ use config::tree::Gitoxide;
+
+ use crate::config::tree::Section;
+ self.resolved
+ .boolean(Gitoxide.name(), None, Gitoxide::TRACE_PACKET.name())
+ .and_then(Result::ok)
+ .unwrap_or_default()
+ }
+
pub(crate) fn personas(&self) -> &identity::Personas {
self.personas
.get_or_init(|| identity::Personas::from_config_and_env(&self.resolved))
@@ -81,17 +183,6 @@ impl Cache {
})
}
- #[cfg(feature = "blob-diff")]
- pub(crate) fn diff_renames(
- &self,
- ) -> Result<Option<crate::object::tree::diff::Rewrites>, crate::object::tree::diff::rewrites::Error> {
- self.diff_renames
- .get_or_try_init(|| {
- crate::object::tree::diff::Rewrites::try_from_config(&self.resolved, self.lenient_config)
- })
- .copied()
- }
-
/// Returns (file-timeout, pack-refs timeout)
pub(crate) fn lock_timeout(
&self,
@@ -189,8 +280,8 @@ impl Cache {
)?;
let capabilities = self.fs_capabilities()?;
let filters = {
- let collection = Default::default();
- let mut filters = gix_filter::Pipeline::new(&collection, crate::filter::Pipeline::options(repo)?);
+ let mut filters =
+ gix_filter::Pipeline::new(repo.command_context()?, crate::filter::Pipeline::options(repo)?);
if let Ok(mut head) = repo.head() {
let ctx = filters.driver_context_mut();
ctx.ref_name = head.referent_name().map(|name| name.as_bstr().to_owned());
diff --git a/vendor/gix/src/config/cache/init.rs b/vendor/gix/src/config/cache/init.rs
index 3c482b154..faf3cc8de 100644
--- a/vendor/gix/src/config/cache/init.rs
+++ b/vendor/gix/src/config/cache/init.rs
@@ -9,7 +9,7 @@ use crate::{
config,
config::{
cache::util::ApplyLeniency,
- tree::{gitoxide, Core, Http},
+ tree::{gitoxide, Core, Gitoxide, Http},
Cache,
},
open,
@@ -143,6 +143,7 @@ impl Cache {
use util::config_bool;
let reflog = util::query_refupdates(&config, lenient_config)?;
+ let refs_namespace = util::query_refs_namespace(&config, lenient_config)?;
let ignore_case = config_bool(&config, &Core::IGNORE_CASE, "core.ignoreCase", false, lenient_config)?;
let use_multi_pack_index = config_bool(
&config,
@@ -166,6 +167,7 @@ impl Cache {
pack_cache_bytes,
object_cache_bytes,
reflog,
+ refs_namespace,
is_bare,
ignore_case,
hex_len,
@@ -222,10 +224,12 @@ impl Cache {
self.object_kind_hint = object_kind_hint;
}
let reflog = util::query_refupdates(config, self.lenient_config)?;
+ let refs_namespace = util::query_refs_namespace(config, self.lenient_config)?;
self.hex_len = hex_len;
self.ignore_case = ignore_case;
self.reflog = reflog;
+ self.refs_namespace = refs_namespace;
self.user_agent = Default::default();
self.personas = Default::default();
@@ -298,6 +302,7 @@ impl crate::Repository {
fn apply_changed_values(&mut self) {
self.refs.write_reflog = util::reflog_or_default(self.config.reflog, self.work_dir().is_some());
+ self.refs.namespace = self.config.refs_namespace.clone();
}
}
@@ -339,6 +344,15 @@ fn apply_environment_overrides(
),
(
"gitoxide",
+ None,
+ git_prefix,
+ &[{
+ let key = &Gitoxide::TRACE_PACKET;
+ (env(key), key.name)
+ }],
+ ),
+ (
+ "gitoxide",
Some(Cow::Borrowed("https".into())),
http_transport,
&[
@@ -377,6 +391,30 @@ fn apply_environment_overrides(
),
(
"gitoxide",
+ Some(Cow::Borrowed("http".into())),
+ git_prefix,
+ &[{
+ let key = &gitoxide::Http::SSL_NO_VERIFY;
+ (env(key), key.name)
+ }],
+ ),
+ (
+ "gitoxide",
+ Some(Cow::Borrowed("credentials".into())),
+ git_prefix,
+ &[
+ {
+ let key = &gitoxide::Credentials::TERMINAL_PROMPT;
+ (env(key), key.name)
+ },
+ {
+ let key = &gitoxide::Credentials::HELPER_STDERR;
+ (env(key), key.name)
+ },
+ ],
+ ),
+ (
+ "gitoxide",
Some(Cow::Borrowed("committer".into())),
identity,
&[
@@ -394,10 +432,20 @@ fn apply_environment_overrides(
"gitoxide",
Some(Cow::Borrowed("core".into())),
git_prefix,
- &[{
- let key = &gitoxide::Core::SHALLOW_FILE;
- (env(key), key.name)
- }],
+ &[
+ {
+ let key = &gitoxide::Core::SHALLOW_FILE;
+ (env(key), key.name)
+ },
+ {
+ let key = &gitoxide::Core::REFS_NAMESPACE;
+ (env(key), key.name)
+ },
+ {
+ let key = &gitoxide::Core::EXTERNAL_COMMAND_STDERR;
+ (env(key), key.name)
+ },
+ ],
),
(
"gitoxide",
@@ -500,6 +548,16 @@ fn apply_environment_overrides(
(env(key), key.name)
}],
),
+ #[cfg(feature = "blob-diff")]
+ (
+ "diff",
+ None,
+ git_prefix,
+ &[{
+ let key = &config::tree::Diff::EXTERNAL;
+ (env(key), key.name)
+ }],
+ ),
] {
let mut section = env_override
.new_section(section_name, subsection_name)
diff --git a/vendor/gix/src/config/cache/util.rs b/vendor/gix/src/config/cache/util.rs
index 4032b2cb1..4c1d6c693 100644
--- a/vendor/gix/src/config/cache/util.rs
+++ b/vendor/gix/src/config/cache/util.rs
@@ -55,6 +55,18 @@ pub(crate) fn query_refupdates(
.map_err(Into::into)
}
+pub(crate) fn query_refs_namespace(
+ config: &gix_config::File<'static>,
+ lenient_config: bool,
+) -> Result<Option<gix_ref::Namespace>, config::refs_namespace::Error> {
+ let key = "gitoxide.core.refsNamespace";
+ config
+ .string_by_key(key)
+ .map(|ns| gitoxide::Core::REFS_NAMESPACE.try_into_refs_namespace(ns))
+ .transpose()
+ .with_leniency(lenient_config)
+}
+
pub(crate) fn reflog_or_default(
config_reflog: Option<gix_ref::store::WriteReflog>,
has_worktree: bool,
diff --git a/vendor/gix/src/config/mod.rs b/vendor/gix/src/config/mod.rs
index 102c7a482..301e19ba2 100644
--- a/vendor/gix/src/config/mod.rs
+++ b/vendor/gix/src/config/mod.rs
@@ -76,6 +76,8 @@ pub enum Error {
ConfigUnsigned(#[from] unsigned_integer::Error),
#[error(transparent)]
ConfigTypedString(#[from] key::GenericErrorWithValue),
+ #[error(transparent)]
+ RefsNamespace(#[from] refs_namespace::Error),
#[error("Cannot handle objects formatted as {:?}", .name)]
UnsupportedObjectFormat { name: BString },
#[error(transparent)]
@@ -91,8 +93,11 @@ pub enum Error {
ResolveIncludes(#[from] gix_config::file::includes::Error),
#[error(transparent)]
FromEnv(#[from] gix_config::file::init::from_env::Error),
- #[error(transparent)]
- PathInterpolation(#[from] gix_config::path::interpolate::Error),
+ #[error("The path {path:?} at the 'core.worktree' configuration could not be interpolated")]
+ PathInterpolation {
+ path: BString,
+ source: gix_config::path::interpolate::Error,
+ },
#[error("{source:?} configuration overrides at open or init time could not be applied.")]
ConfigOverrides {
#[source]
@@ -117,6 +122,36 @@ pub mod diff {
Unimplemented { name: BString },
}
}
+
+ ///
+ pub mod pipeline_options {
+ /// The error produced when obtaining options needed to fill in [gix_diff::blob::pipeline::Options].
+ #[derive(Debug, thiserror::Error)]
+ #[allow(missing_docs)]
+ pub enum Error {
+ #[error(transparent)]
+ FilesystemCapabilities(#[from] crate::config::boolean::Error),
+ #[error(transparent)]
+ BigFileThreshold(#[from] crate::config::unsigned_integer::Error),
+ }
+ }
+
+ ///
+ pub mod drivers {
+ use crate::bstr::BString;
+
+ /// The error produced when obtaining a list of [Drivers](gix_diff::blob::Driver).
+ #[derive(Debug, thiserror::Error)]
+ #[error("Failed to parse value of 'diff.{name}.{attribute}'")]
+ pub struct Error {
+ /// The name fo the driver.
+ pub name: BString,
+ /// The name of the attribute we tried to parse.
+ pub attribute: &'static str,
+ /// The actual error that occurred.
+ pub source: Box<dyn std::error::Error + Send + Sync + 'static>,
+ }
+ }
}
///
@@ -149,6 +184,25 @@ pub mod checkout_options {
Attributes(#[from] super::attribute_stack::Error),
#[error(transparent)]
FilterPipelineOptions(#[from] crate::filter::pipeline::options::Error),
+ #[error(transparent)]
+ CommandContext(#[from] crate::config::command_context::Error),
+ }
+}
+
+///
+#[cfg(feature = "attributes")]
+pub mod command_context {
+ use crate::config;
+
+ /// The error produced when collecting all information relevant to spawned commands,
+ /// obtained via [Repository::command_context()](crate::Repository::command_context()).
+ #[derive(Debug, thiserror::Error)]
+ #[allow(missing_docs)]
+ pub enum Error {
+ #[error(transparent)]
+ Boolean(#[from] config::boolean::Error),
+ #[error(transparent)]
+ ParseBool(#[from] gix_config::value::Error),
}
}
@@ -409,6 +463,12 @@ pub mod refspec {
}
///
+pub mod refs_namespace {
+ /// The error produced when failing to parse a refspec from the configuration.
+ pub type Error = super::key::Error<gix_validate::reference::name::Error, 'v', 'i'>;
+}
+
+///
pub mod ssl_version {
/// The error produced when failing to parse a refspec from the configuration.
pub type Error = super::key::Error<std::convert::Infallible, 's', 'i'>;
@@ -504,6 +564,8 @@ pub(crate) struct Cache {
pub use_multi_pack_index: bool,
/// The representation of `core.logallrefupdates`, or `None` if the variable wasn't set.
pub reflog: Option<gix_ref::store::WriteReflog>,
+ /// The representation of `gitoxide.core.refsNamespace`, or `None` if the variable wasn't set.
+ pub refs_namespace: Option<gix_ref::Namespace>,
/// The configured user agent for presentation to servers.
pub(crate) user_agent: OnceCell<String>,
/// identities for later use, lazy initialization.
@@ -512,7 +574,7 @@ pub(crate) struct Cache {
pub(crate) url_rewrite: OnceCell<crate::remote::url::Rewrite>,
/// The lazy-loaded rename information for diffs.
#[cfg(feature = "blob-diff")]
- pub(crate) diff_renames: OnceCell<Option<crate::object::tree::diff::Rewrites>>,
+ pub(crate) diff_renames: OnceCell<Option<crate::diff::Rewrites>>,
/// A lazily loaded mapping to know which url schemes to allow
#[cfg(any(feature = "blocking-network-client", feature = "async-network-client"))]
pub(crate) url_scheme: OnceCell<crate::remote::url::SchemePermission>,
@@ -541,3 +603,23 @@ pub(crate) struct Cache {
environment: crate::open::permissions::Environment,
// TODO: make core.precomposeUnicode available as well.
}
+
+/// Utillities shared privately across the crate, for lack of a better place.
+pub(crate) mod shared {
+ use crate::{
+ config,
+ config::{cache::util::ApplyLeniency, tree::Core},
+ };
+
+ pub fn is_replace_refs_enabled(
+ config: &gix_config::File<'static>,
+ lenient: bool,
+ mut filter_config_section: fn(&gix_config::file::Metadata) -> bool,
+ ) -> Result<Option<bool>, config::boolean::Error> {
+ config
+ .boolean_filter_by_key("core.useReplaceRefs", &mut filter_config_section)
+ .map(|b| Core::USE_REPLACE_REFS.enrich_error(b))
+ .transpose()
+ .with_leniency(lenient)
+ }
+}
diff --git a/vendor/gix/src/config/snapshot/access.rs b/vendor/gix/src/config/snapshot/access.rs
index 7dc593880..6b98cf42d 100644
--- a/vendor/gix/src/config/snapshot/access.rs
+++ b/vendor/gix/src/config/snapshot/access.rs
@@ -4,9 +4,8 @@ use std::borrow::Cow;
use gix_features::threading::OwnShared;
use gix_macros::momo;
-use crate::bstr::ByteSlice;
use crate::{
- bstr::{BStr, BString},
+ bstr::{BStr, BString, ByteSlice},
config::{CommitAutoRollback, Snapshot, SnapshotMut},
};
@@ -118,9 +117,13 @@ impl<'repo> SnapshotMut<'repo> {
}
let value = new_value.into();
key.validate(value)?;
- let current = self
- .config
- .set_raw_value(key.section().name(), None, key.name(), value)?;
+ let section = key.section();
+ let current = match section.parent() {
+ Some(parent) => self
+ .config
+ .set_raw_value(parent.name(), Some(section.name().into()), key.name(), value)?,
+ None => self.config.set_raw_value(section.name(), None, key.name(), value)?,
+ };
Ok(current.map(std::borrow::Cow::into_owned))
}
diff --git a/vendor/gix/src/config/snapshot/credential_helpers.rs b/vendor/gix/src/config/snapshot/credential_helpers.rs
index 189e74471..54499a1c3 100644
--- a/vendor/gix/src/config/snapshot/credential_helpers.rs
+++ b/vendor/gix/src/config/snapshot/credential_helpers.rs
@@ -2,11 +2,12 @@ use std::{borrow::Cow, convert::TryFrom};
pub use error::Error;
-use crate::config::cache::util::IgnoreEmptyPath;
+use crate::config::cache::util::ApplyLeniency;
use crate::{
bstr::{ByteSlice, ByteVec},
config::{
- tree::{credential, Core, Credential, Key},
+ cache::util::IgnoreEmptyPath,
+ tree::{credential, gitoxide::Credentials, Core, Credential, Key},
Snapshot,
},
};
@@ -25,6 +26,8 @@ mod error {
},
#[error("core.askpass could not be read")]
CoreAskpass(#[from] gix_config::path::interpolate::Error),
+ #[error(transparent)]
+ BooleanConfig(#[from] crate::config::boolean::Error),
}
}
@@ -144,16 +147,27 @@ impl Snapshot<'_> {
.transpose()
.ignore_empty()?
.map(|c| Cow::Owned(c.into_owned())),
- ..Default::default()
+ mode: self
+ .try_boolean(Credentials::TERMINAL_PROMPT.logical_name().as_str())
+ .map(|val| Credentials::TERMINAL_PROMPT.enrich_error(val))
+ .transpose()
+ .with_leniency(self.repo.config.lenient_config)?
+ .and_then(|val| (!val).then_some(gix_prompt::Mode::Disable))
+ .unwrap_or_default(),
}
- .apply_environment(allow_git_env, allow_ssh_env, allow_git_env);
+ .apply_environment(allow_git_env, allow_ssh_env, false /* terminal prompt */);
Ok((
gix_credentials::helper::Cascade {
programs,
use_http_path,
// The default ssh implementation uses binaries that do their own auth, so our passwords aren't used.
query_user_only: url.scheme == gix_url::Scheme::Ssh,
- ..Default::default()
+ stderr: self
+ .try_boolean(Credentials::HELPER_STDERR.logical_name().as_str())
+ .map(|val| Credentials::HELPER_STDERR.enrich_error(val))
+ .transpose()
+ .with_leniency(self.repo.options.lenient_config)?
+ .unwrap_or(true),
},
gix_credentials::helper::Action::get_for_url(url.to_bstring()),
prompt_options,
diff --git a/vendor/gix/src/config/tree/sections/core.rs b/vendor/gix/src/config/tree/sections/core.rs
index 2ec5c279e..15ad9f947 100644
--- a/vendor/gix/src/config/tree/sections/core.rs
+++ b/vendor/gix/src/config/tree/sections/core.rs
@@ -8,13 +8,16 @@ impl Core {
pub const ABBREV: Abbrev = Abbrev::new_with_validate("abbrev", &config::Tree::CORE, validate::Abbrev);
/// The `core.bare` key.
pub const BARE: keys::Boolean = keys::Boolean::new_boolean("bare", &config::Tree::CORE);
+ /// The `core.bigFileThreshold` key.
+ pub const BIG_FILE_THRESHOLD: keys::UnsignedInteger =
+ keys::UnsignedInteger::new_unsigned_integer("bigFileThreshold", &config::Tree::CORE);
/// The `core.checkStat` key.
pub const CHECK_STAT: CheckStat =
CheckStat::new_with_validate("checkStat", &config::Tree::CORE, validate::CheckStat);
/// The `core.deltaBaseCacheLimit` key.
pub const DELTA_BASE_CACHE_LIMIT: keys::UnsignedInteger =
keys::UnsignedInteger::new_unsigned_integer("deltaBaseCacheLimit", &config::Tree::CORE)
- .with_environment_override("GITOXIDE_PACK_CACHE_MEMORY")
+ .with_environment_override("GIX_PACK_CACHE_MEMORY")
.with_note("if unset, we default to a small 64 slot fixed-size cache that holds at most 64 full delta base objects of any size. Set to 0 to deactivate it entirely");
/// The `core.disambiguate` key.
pub const DISAMBIGUATE: Disambiguate =
@@ -95,6 +98,7 @@ impl Section for Core {
&[
&Self::ABBREV,
&Self::BARE,
+ &Self::BIG_FILE_THRESHOLD,
&Self::CHECK_STAT,
&Self::DELTA_BASE_CACHE_LIMIT,
&Self::DISAMBIGUATE,
diff --git a/vendor/gix/src/config/tree/sections/diff.rs b/vendor/gix/src/config/tree/sections/diff.rs
index 7c467b8f5..0ebc13711 100644
--- a/vendor/gix/src/config/tree/sections/diff.rs
+++ b/vendor/gix/src/config/tree/sections/diff.rs
@@ -1,6 +1,6 @@
use crate::{
config,
- config::tree::{keys, Diff, Key, Section},
+ config::tree::{keys, Diff, Key, Section, SubSectionRequirement},
};
impl Diff {
@@ -17,6 +17,24 @@ impl Diff {
);
/// The `diff.renames` key.
pub const RENAMES: Renames = Renames::new_renames("renames", &config::Tree::DIFF);
+
+ /// The `diff.<driver>.command` key.
+ pub const DRIVER_COMMAND: keys::String = keys::String::new_string("command", &config::Tree::DIFF)
+ .with_subsection_requirement(Some(SubSectionRequirement::Parameter("driver")));
+ /// The `diff.<driver>.textconv` key.
+ pub const DRIVER_TEXTCONV: keys::String = keys::String::new_string("textconv", &config::Tree::DIFF)
+ .with_subsection_requirement(Some(SubSectionRequirement::Parameter("driver")));
+ /// The `diff.<driver>.algorithm` key.
+ pub const DRIVER_ALGORITHM: Algorithm =
+ Algorithm::new_with_validate("algorithm", &config::Tree::DIFF, validate::Algorithm)
+ .with_subsection_requirement(Some(SubSectionRequirement::Parameter("driver")));
+ /// The `diff.<driver>.binary` key.
+ pub const DRIVER_BINARY: Binary = Binary::new_with_validate("binary", &config::Tree::DIFF, validate::Binary)
+ .with_subsection_requirement(Some(SubSectionRequirement::Parameter("driver")));
+
+ /// The `diff.external` key.
+ pub const EXTERNAL: keys::Program =
+ keys::Program::new_program("external", &config::Tree::DIFF).with_environment_override("GIT_EXTERNAL_DIFF");
}
impl Section for Diff {
@@ -25,7 +43,16 @@ impl Section for Diff {
}
fn keys(&self) -> &[&dyn Key] {
- &[&Self::ALGORITHM, &Self::RENAME_LIMIT, &Self::RENAMES]
+ &[
+ &Self::ALGORITHM,
+ &Self::RENAME_LIMIT,
+ &Self::RENAMES,
+ &Self::DRIVER_COMMAND,
+ &Self::DRIVER_TEXTCONV,
+ &Self::DRIVER_ALGORITHM,
+ &Self::DRIVER_BINARY,
+ &Self::EXTERNAL,
+ ]
}
}
@@ -35,6 +62,9 @@ pub type Algorithm = keys::Any<validate::Algorithm>;
/// The `diff.renames` key.
pub type Renames = keys::Any<validate::Renames>;
+/// The `diff.<driver>.binary` key.
+pub type Binary = keys::Any<validate::Binary>;
+
mod algorithm {
use std::borrow::Cow;
@@ -67,6 +97,38 @@ mod algorithm {
}
}
+mod binary {
+ use crate::config::tree::diff::Binary;
+
+ impl Binary {
+ /// Convert `value` into a tri-state boolean that can take the special value `auto`, resulting in `None`, or is a boolean.
+ /// If `None` is given, it's treated as implicit boolean `true`, as this method is made to be used
+ /// with [`gix_config::file::section::Body::value_implicit()`].
+ pub fn try_into_binary(
+ &'static self,
+ value: Option<std::borrow::Cow<'_, crate::bstr::BStr>>,
+ ) -> Result<Option<bool>, crate::config::key::GenericErrorWithValue> {
+ Ok(match value {
+ None => Some(true),
+ Some(value) => {
+ if value.as_ref() == "auto" {
+ None
+ } else {
+ Some(
+ gix_config::Boolean::try_from(value.as_ref())
+ .map(|b| b.0)
+ .map_err(|err| {
+ crate::config::key::GenericErrorWithValue::from_value(self, value.into_owned())
+ .with_source(err)
+ })?,
+ )
+ }
+ }
+ })
+ }
+ }
+}
+
mod renames {
use crate::{
bstr::ByteSlice,
@@ -125,4 +187,12 @@ mod validate {
Ok(())
}
}
+
+ pub struct Binary;
+ impl keys::Validate for Binary {
+ fn validate(&self, value: &BStr) -> Result<(), Box<dyn std::error::Error + Send + Sync + 'static>> {
+ Diff::DRIVER_BINARY.try_into_binary(Some(value.into()))?;
+ Ok(())
+ }
+ }
}
diff --git a/vendor/gix/src/config/tree/sections/fetch.rs b/vendor/gix/src/config/tree/sections/fetch.rs
index 32db7be5f..9b618913d 100644
--- a/vendor/gix/src/config/tree/sections/fetch.rs
+++ b/vendor/gix/src/config/tree/sections/fetch.rs
@@ -45,8 +45,7 @@ mod algorithm {
&'static self,
name: std::borrow::Cow<'_, crate::bstr::BStr>,
) -> Result<crate::remote::fetch::negotiate::Algorithm, crate::config::key::GenericErrorWithValue> {
- use crate::bstr::ByteSlice;
- use crate::remote::fetch::negotiate::Algorithm;
+ use crate::{bstr::ByteSlice, remote::fetch::negotiate::Algorithm};
Ok(match name.as_ref().as_bytes() {
b"noop" => Algorithm::Noop,
diff --git a/vendor/gix/src/config/tree/sections/gitoxide.rs b/vendor/gix/src/config/tree/sections/gitoxide.rs
index 966d5af7c..a3b054412 100644
--- a/vendor/gix/src/config/tree/sections/gitoxide.rs
+++ b/vendor/gix/src/config/tree/sections/gitoxide.rs
@@ -14,6 +14,8 @@ impl Gitoxide {
pub const COMMIT: Commit = Commit;
/// The `gitoxide.committer` section.
pub const COMMITTER: Committer = Committer;
+ /// The `gitoxide.credentials` section.
+ pub const CREDENTIALS: Credentials = Credentials;
/// The `gitoxide.http` section.
pub const HTTP: Http = Http;
/// The `gitoxide.https` section.
@@ -31,6 +33,9 @@ impl Gitoxide {
pub const USER_AGENT: keys::Any = keys::Any::new("userAgent", &config::Tree::GITOXIDE).with_note(
"The user agent presented on the git protocol layer, serving as fallback for when no `http.userAgent` is set",
);
+ /// The `gitoxide.tracePacket` Key.
+ pub const TRACE_PACKET: keys::Boolean = keys::Boolean::new_boolean("tracePacket", &config::Tree::GITOXIDE)
+ .with_environment_override("GIT_TRACE_PACKET");
}
impl Section for Gitoxide {
@@ -39,7 +44,7 @@ impl Section for Gitoxide {
}
fn keys(&self) -> &[&dyn Key] {
- &[&Self::USER_AGENT]
+ &[&Self::USER_AGENT, &Self::TRACE_PACKET]
}
fn sub_sections(&self) -> &[&dyn Section] {
@@ -49,6 +54,7 @@ impl Section for Gitoxide {
&Self::CORE,
&Self::COMMIT,
&Self::COMMITTER,
+ &Self::CREDENTIALS,
&Self::HTTP,
&Self::HTTPS,
&Self::OBJECTS,
@@ -69,6 +75,20 @@ mod subsections {
#[derive(Copy, Clone, Default)]
pub struct Core;
+ /// The `gitoxide.allow.protocolFromUser` key.
+ pub type RefsNamespace = keys::Any<super::validate::RefsNamespace>;
+
+ impl RefsNamespace {
+ /// Derive the negotiation algorithm identified by `name`, case-sensitively.
+ pub fn try_into_refs_namespace(
+ &'static self,
+ name: std::borrow::Cow<'_, crate::bstr::BStr>,
+ ) -> Result<gix_ref::Namespace, crate::config::refs_namespace::Error> {
+ gix_ref::namespace::expand(name.as_ref())
+ .map_err(|err| crate::config::key::Error::from_value(self, name.into_owned()).with_source(err))
+ }
+ }
+
impl Core {
/// The `gitoxide.core.defaultPackCacheMemoryLimit` key.
pub const DEFAULT_PACK_CACHE_MEMORY_LIMIT: keys::UnsignedInteger =
@@ -95,6 +115,20 @@ mod subsections {
/// It controls whether or not long running filter driver processes can use the 'delay' capability.
pub const FILTER_PROCESS_DELAY: keys::Boolean =
keys::Boolean::new_boolean("filterProcessDelay", &Gitoxide::CORE);
+
+ /// The `gitoxide.core.externalCommandStderr` key (default `true`).
+ ///
+ /// If `true`, the default, `stderr` of worktree filter programs, or any other git-context bearing command
+ /// invoked will be inherited.
+ /// If `false`, it will be suppressed completely.
+ pub const EXTERNAL_COMMAND_STDERR: keys::Boolean =
+ keys::Boolean::new_boolean("externalCommandStderr", &Gitoxide::CORE)
+ .with_environment_override("GIX_EXTERNAL_COMMAND_STDERR");
+
+ /// The `gitoxide.core.refsNamespace` key.
+ pub const REFS_NAMESPACE: RefsNamespace =
+ keys::Any::new_with_validate("refsNamespace", &Gitoxide::CORE, super::validate::RefsNamespace)
+ .with_environment_override("GIT_NAMESPACE");
}
impl Section for Core {
@@ -109,6 +143,8 @@ mod subsections {
&Self::USE_STDEV,
&Self::SHALLOW_FILE,
&Self::FILTER_PROCESS_DELAY,
+ &Self::EXTERNAL_COMMAND_STDERR,
+ &Self::REFS_NAMESPACE,
]
}
@@ -154,6 +190,14 @@ mod subsections {
http::SslVersion::new_ssl_version("sslVersionMax", &Gitoxide::HTTP).with_note(
"entirely new to set the upper bound for the allowed ssl version range. Overwrites the max bound of `http.sslVersion` if set. Min and Max must be set to become effective.",
);
+ /// The `gitoxide.http.sslNoVerify` key.
+ ///
+ /// If set, disable SSL verification. Using this is discouraged as it can lead to
+ /// various security risks. An example where this may be needed is when an internal
+ /// git server uses a self-signed certificate and the user accepts the associated security risks.
+ pub const SSL_NO_VERIFY: keys::Boolean = keys::Boolean::new_boolean("sslNoVerify", &Gitoxide::HTTP)
+ .with_environment_override("GIT_SSL_NO_VERIFY")
+ .with_note("used to disable SSL verification. When this is enabled it takes priority over http.sslVerify");
/// The `gitoxide.http.proxyAuthMethod` key.
pub const PROXY_AUTH_METHOD: http::ProxyAuthMethod =
http::ProxyAuthMethod::new_proxy_auth_method("proxyAuthMethod", &Gitoxide::HTTP)
@@ -174,6 +218,7 @@ mod subsections {
&Self::CONNECT_TIMEOUT,
&Self::SSL_VERSION_MIN,
&Self::SSL_VERSION_MAX,
+ &Self::SSL_NO_VERIFY,
&Self::PROXY_AUTH_METHOD,
]
}
@@ -375,7 +420,7 @@ mod subsections {
pub const CACHE_LIMIT: keys::UnsignedInteger =
keys::UnsignedInteger::new_unsigned_integer("cacheLimit", &Gitoxide::OBJECTS)
.with_note("If unset or 0, there is no object cache")
- .with_environment_override("GITOXIDE_OBJECT_CACHE_MEMORY");
+ .with_environment_override("GIX_OBJECT_CACHE_MEMORY");
/// The `gitoxide.objects.noReplace` key.
pub const NO_REPLACE: keys::Boolean = keys::Boolean::new_boolean("noReplace", &Gitoxide::OBJECTS);
/// The `gitoxide.objects.replaceRefBase` key.
@@ -424,6 +469,37 @@ mod subsections {
}
}
+ /// The `credentials` sub-section.
+ #[derive(Copy, Clone, Default)]
+ pub struct Credentials;
+ impl Credentials {
+ /// The `gitoxide.credentials.terminalPrompt` key.
+ pub const TERMINAL_PROMPT: keys::Boolean = keys::Boolean::new_boolean("terminalPrompt", &Gitoxide::CREDENTIALS)
+ .with_note("This is a custom addition to provide an alternative to the respective environment variable.")
+ .with_environment_override("GIT_TERMINAL_PROMPT");
+
+ /// The `gitoxide.credentials.helperStderr` key to control what happens with the credential helpers `stderr`.
+ ///
+ /// If `true`, the default, `stderr` of credential helper programs will be inherited, just like with `git`.
+ /// If `false`, will be suppressed completely.
+ pub const HELPER_STDERR: keys::Boolean = keys::Boolean::new_boolean("helperStderr", &Gitoxide::CREDENTIALS)
+ .with_environment_override("GIX_CREDENTIALS_HELPER_STDERR");
+ }
+
+ impl Section for Credentials {
+ fn name(&self) -> &str {
+ "credentials"
+ }
+
+ fn keys(&self) -> &[&dyn Key] {
+ &[&Self::TERMINAL_PROMPT, &Self::HELPER_STDERR]
+ }
+
+ fn parent(&self) -> Option<&dyn Section> {
+ Some(&Tree::GITOXIDE)
+ }
+ }
+
/// The `commit` sub-section.
#[derive(Copy, Clone, Default)]
pub struct Commit;
@@ -451,7 +527,7 @@ mod subsections {
}
}
}
-pub use subsections::{Allow, Author, Commit, Committer, Core, Http, Https, Objects, Pathspec, Ssh, User};
+pub use subsections::{Allow, Author, Commit, Committer, Core, Credentials, Http, Https, Objects, Pathspec, Ssh, User};
pub mod validate {
use std::error::Error;
@@ -467,4 +543,12 @@ pub mod validate {
Ok(())
}
}
+
+ pub struct RefsNamespace;
+ impl Validate for RefsNamespace {
+ fn validate(&self, value: &BStr) -> Result<(), Box<dyn Error + Send + Sync + 'static>> {
+ super::Core::REFS_NAMESPACE.try_into_refs_namespace(value.into())?;
+ Ok(())
+ }
+ }
}
diff --git a/vendor/gix/src/config/tree/sections/http.rs b/vendor/gix/src/config/tree/sections/http.rs
index f45c37076..4fc733564 100644
--- a/vendor/gix/src/config/tree/sections/http.rs
+++ b/vendor/gix/src/config/tree/sections/http.rs
@@ -10,6 +10,9 @@ impl Http {
.with_deviation(
"accepts the new 'default' value which means to use the curl default just like the empty string does",
);
+ /// The `http.sslVerify` key.
+ pub const SSL_VERIFY: keys::Boolean = keys::Boolean::new_boolean("sslVerify", &config::Tree::HTTP)
+ .with_note("also see the `gitoxide.http.sslNoVerify` key");
/// The `http.proxy` key.
pub const PROXY: keys::String =
keys::String::new_string("proxy", &config::Tree::HTTP).with_deviation("fails on strings with illformed UTF-8");
@@ -58,6 +61,7 @@ impl Section for Http {
fn keys(&self) -> &[&dyn Key] {
&[
&Self::SSL_VERSION,
+ &Self::SSL_VERIFY,
&Self::PROXY,
&Self::PROXY_AUTH_METHOD,
&Self::VERSION,
diff --git a/vendor/gix/src/create.rs b/vendor/gix/src/create.rs
index 1ef63b9aa..00971ec05 100644
--- a/vendor/gix/src/create.rs
+++ b/vendor/gix/src/create.rs
@@ -230,7 +230,7 @@ pub fn into(
Ok(gix_discover::repository::Path::from_dot_git_dir(
dot_git,
if bare {
- gix_discover::repository::Kind::Bare
+ gix_discover::repository::Kind::PossiblyBare
} else {
gix_discover::repository::Kind::WorkTree { linked_git_dir: None }
},
diff --git a/vendor/gix/src/diff.rs b/vendor/gix/src/diff.rs
index b10819293..af3c98704 100644
--- a/vendor/gix/src/diff.rs
+++ b/vendor/gix/src/diff.rs
@@ -15,3 +15,130 @@ pub mod rename {
RenamesAndCopies,
}
}
+
+///
+#[cfg(feature = "blob-diff")]
+mod utils {
+ use gix_diff::{rewrites::Copies, Rewrites};
+
+ use crate::{
+ config::{cache::util::ApplyLeniency, tree::Diff},
+ diff::rename::Tracking,
+ Repository,
+ };
+
+ ///
+ pub mod new_rewrites {
+ /// The error returned by [`new_rewrites()`](super::new_rewrites()).
+ #[derive(Debug, thiserror::Error)]
+ #[allow(missing_docs)]
+ pub enum Error {
+ #[error(transparent)]
+ ConfigDiffRenames(#[from] crate::config::key::GenericError),
+ #[error(transparent)]
+ ConfigDiffRenameLimit(#[from] crate::config::unsigned_integer::Error),
+ }
+ }
+
+ ///
+ pub mod resource_cache {
+ /// The error returned by [`resource_cache()`](super::resource_cache()).
+ #[derive(Debug, thiserror::Error)]
+ #[allow(missing_docs)]
+ pub enum Error {
+ #[error(transparent)]
+ DiffAlgorithm(#[from] crate::config::diff::algorithm::Error),
+ #[error(transparent)]
+ WorktreeFilterOptions(#[from] crate::filter::pipeline::options::Error),
+ #[error(transparent)]
+ DiffDrivers(#[from] crate::config::diff::drivers::Error),
+ #[error(transparent)]
+ DiffPipelineOptions(#[from] crate::config::diff::pipeline_options::Error),
+ #[error(transparent)]
+ CommandContext(#[from] crate::config::command_context::Error),
+ #[error(transparent)]
+ AttributeStack(#[from] crate::config::attribute_stack::Error),
+ }
+ }
+
+ /// Create an instance by reading all relevant information from the `config`uration, while being `lenient` or not.
+ /// Returns `Ok(None)` if nothing is configured.
+ ///
+ /// Note that missing values will be defaulted similar to what git does.
+ #[allow(clippy::result_large_err)]
+ pub fn new_rewrites(
+ config: &gix_config::File<'static>,
+ lenient: bool,
+ ) -> Result<Option<Rewrites>, new_rewrites::Error> {
+ let key = "diff.renames";
+ let copies = match config
+ .boolean_by_key(key)
+ .map(|value| Diff::RENAMES.try_into_renames(value))
+ .transpose()
+ .with_leniency(lenient)?
+ {
+ Some(renames) => match renames {
+ Tracking::Disabled => return Ok(None),
+ Tracking::Renames => None,
+ Tracking::RenamesAndCopies => Some(Copies::default()),
+ },
+ None => return Ok(None),
+ };
+
+ let default = Rewrites::default();
+ Ok(Rewrites {
+ copies,
+ limit: config
+ .integer_by_key("diff.renameLimit")
+ .map(|value| Diff::RENAME_LIMIT.try_into_usize(value))
+ .transpose()
+ .with_leniency(lenient)?
+ .unwrap_or(default.limit),
+ ..default
+ }
+ .into())
+ }
+
+ /// Return a low-level utility to efficiently prepare a the blob-level diff operation between two resources,
+ /// and cache these diffable versions so that matrix-like MxN diffs are efficient.
+ ///
+ /// `repo` is used to obtain the needed configuration values, and `index` is used to potentially read `.gitattributes`
+ /// files from which may affect the diff operation.
+ /// `mode` determines how the diffable files will look like, and also how fast, in average, these conversions are.
+ /// `attribute_source` controls where `.gitattributes` will be read from, and it's typically adjusted based on the
+ /// `roots` - if there are no worktree roots, `.gitattributes` are also not usually read from worktrees.
+ /// `roots` provide information about where to get diffable data from, so source and destination can either be sourced from
+ /// a worktree, or from the object database, or both.
+ pub fn resource_cache(
+ repo: &Repository,
+ index: &gix_index::State,
+ mode: gix_diff::blob::pipeline::Mode,
+ attribute_source: gix_worktree::stack::state::attributes::Source,
+ roots: gix_diff::blob::pipeline::WorktreeRoots,
+ ) -> Result<gix_diff::blob::Platform, resource_cache::Error> {
+ let diff_algo = repo.config.diff_algorithm()?;
+ let diff_cache = gix_diff::blob::Platform::new(
+ gix_diff::blob::platform::Options {
+ algorithm: Some(diff_algo),
+ skip_internal_diff_if_external_is_configured: false,
+ },
+ gix_diff::blob::Pipeline::new(
+ roots,
+ gix_filter::Pipeline::new(repo.command_context()?, crate::filter::Pipeline::options(repo)?),
+ repo.config.diff_drivers()?,
+ repo.config.diff_pipeline_options()?,
+ ),
+ mode,
+ repo.attributes_only(
+ // TODO(perf): this could benefit from not having to build an intermediate index,
+ // and traverse the a tree directly.
+ index,
+ attribute_source,
+ )?
+ .inner,
+ );
+ Ok(diff_cache)
+ }
+}
+#[cfg(feature = "blob-diff")]
+pub use utils::{new_rewrites, resource_cache};
diff --git a/vendor/gix/src/ext/object_id.rs b/vendor/gix/src/ext/object_id.rs
index a4515022b..d4d946766 100644
--- a/vendor/gix/src/ext/object_id.rs
+++ b/vendor/gix/src/ext/object_id.rs
@@ -8,10 +8,9 @@ pub type AncestorsIter<Find> = Ancestors<Find, fn(&gix_hash::oid) -> bool, ances
/// An extension trait to add functionality to [`ObjectId`]s.
pub trait ObjectIdExt: Sealed {
/// Create an iterator over the ancestry of the commits reachable from this id, which must be a commit.
- fn ancestors<Find, E>(self, find: Find) -> AncestorsIter<Find>
+ fn ancestors<Find>(self, find: Find) -> AncestorsIter<Find>
where
- Find: for<'a> FnMut(&gix_hash::oid, &'a mut Vec<u8>) -> Result<gix_object::CommitRefIter<'a>, E>,
- E: std::error::Error + Send + Sync + 'static;
+ Find: gix_object::Find;
/// Infuse this object id `repo` access.
fn attach(self, repo: &crate::Repository) -> crate::Id<'_>;
@@ -20,10 +19,9 @@ pub trait ObjectIdExt: Sealed {
impl Sealed for ObjectId {}
impl ObjectIdExt for ObjectId {
- fn ancestors<Find, E>(self, find: Find) -> AncestorsIter<Find>
+ fn ancestors<Find>(self, find: Find) -> AncestorsIter<Find>
where
- Find: for<'a> FnMut(&gix_hash::oid, &'a mut Vec<u8>) -> Result<gix_object::CommitRefIter<'a>, E>,
- E: std::error::Error + Send + Sync + 'static,
+ Find: gix_object::Find,
{
Ancestors::new(Some(self), ancestors::State::default(), find)
}
diff --git a/vendor/gix/src/ext/rev_spec.rs b/vendor/gix/src/ext/rev_spec.rs
index ed7dc0460..caa58e2c7 100644
--- a/vendor/gix/src/ext/rev_spec.rs
+++ b/vendor/gix/src/ext/rev_spec.rs
@@ -12,6 +12,7 @@ impl RevSpecExt for gix_revision::Spec {
fn attach(self, repo: &crate::Repository) -> crate::revision::Spec<'_> {
crate::revision::Spec {
inner: self,
+ path: None,
first_ref: None,
second_ref: None,
repo,
diff --git a/vendor/gix/src/ext/tree.rs b/vendor/gix/src/ext/tree.rs
index 56b832b84..9aacc9d58 100644
--- a/vendor/gix/src/ext/tree.rs
+++ b/vendor/gix/src/ext/tree.rs
@@ -1,6 +1,5 @@
use std::borrow::BorrowMut;
-use gix_hash::oid;
use gix_object::TreeRefIter;
use gix_traverse::tree::breadthfirst;
@@ -16,11 +15,11 @@ pub trait TreeIterExt: Sealed {
fn traverse<StateMut, Find, V>(
&self,
state: StateMut,
- find: Find,
+ objects: Find,
delegate: &mut V,
) -> Result<(), breadthfirst::Error>
where
- Find: for<'a> FnMut(&oid, &'a mut Vec<u8>) -> Option<TreeRefIter<'a>>,
+ Find: gix_object::Find,
StateMut: BorrowMut<breadthfirst::State>,
V: gix_traverse::tree::Visit;
}
@@ -31,15 +30,15 @@ impl<'d> TreeIterExt for TreeRefIter<'d> {
fn traverse<StateMut, Find, V>(
&self,
state: StateMut,
- find: Find,
+ objects: Find,
delegate: &mut V,
) -> Result<(), breadthfirst::Error>
where
- Find: for<'a> FnMut(&oid, &'a mut Vec<u8>) -> Option<TreeRefIter<'a>>,
+ Find: gix_object::Find,
StateMut: BorrowMut<breadthfirst::State>,
V: gix_traverse::tree::Visit,
{
- breadthfirst(self.clone(), state, find, delegate)
+ breadthfirst(*self, state, objects, delegate)
}
}
diff --git a/vendor/gix/src/filter.rs b/vendor/gix/src/filter.rs
index b106840a7..f05e332a2 100644
--- a/vendor/gix/src/filter.rs
+++ b/vendor/gix/src/filter.rs
@@ -2,7 +2,7 @@
use std::borrow::Cow;
pub use gix_filter as plumbing;
-use gix_odb::{Find, FindExt};
+use gix_object::Find;
use crate::{
bstr::BStr,
@@ -32,6 +32,8 @@ pub mod pipeline {
name: BString,
source: gix_config::value::Error,
},
+ #[error(transparent)]
+ CommandContext(#[from] config::command_context::Error),
}
}
@@ -111,7 +113,7 @@ impl<'repo> Pipeline<'repo> {
/// Create a new instance by extracting all necessary information and configuration from a `repo` along with `cache` for accessing
/// attributes. The `index` is used for some filters which may access it under very specific circumstances.
pub fn new(repo: &'repo Repository, cache: gix_worktree::Stack) -> Result<Self, pipeline::options::Error> {
- let pipeline = gix_filter::Pipeline::new(cache.attributes_collection(), Self::options(repo)?);
+ let pipeline = gix_filter::Pipeline::new(repo.command_context()?, Self::options(repo)?);
Ok(Pipeline {
inner: pipeline,
cache,
@@ -141,16 +143,14 @@ impl<'repo> Pipeline<'repo> {
where
R: std::io::Read,
{
- let entry = self
- .cache
- .at_path(rela_path, Some(false), |id, buf| self.repo.objects.find_blob(id, buf))?;
+ let entry = self.cache.at_path(rela_path, Some(false), &self.repo.objects)?;
Ok(self.inner.convert_to_git(
src,
rela_path,
&mut |_, attrs| {
entry.matching_attributes(attrs);
},
- &mut |buf| -> Result<_, gix_odb::find::Error> {
+ &mut |buf| -> Result<_, gix_object::find::Error> {
let entry = match index.entry_by_path(gix_path::into_bstr(rela_path).as_ref()) {
None => return Ok(None),
Some(entry) => entry,
@@ -175,9 +175,7 @@ impl<'repo> Pipeline<'repo> {
can_delay: gix_filter::driver::apply::Delay,
) -> Result<gix_filter::pipeline::convert::ToWorktreeOutcome<'input, '_>, pipeline::convert_to_worktree::Error>
{
- let entry = self
- .cache
- .at_entry(rela_path, Some(false), |id, buf| self.repo.objects.find_blob(id, buf))?;
+ let entry = self.cache.at_entry(rela_path, Some(false), &self.repo.objects)?;
Ok(self.inner.convert_to_worktree(
src,
rela_path,
diff --git a/vendor/gix/src/head/peel.rs b/vendor/gix/src/head/peel.rs
index 88e23636f..4ee116ed0 100644
--- a/vendor/gix/src/head/peel.rs
+++ b/vendor/gix/src/head/peel.rs
@@ -1,12 +1,14 @@
use crate::{
ext::{ObjectIdExt, ReferenceExt},
+ head::Kind,
Head,
};
mod error {
use crate::{object, reference};
- /// The error returned by [`Head::peel_to_id_in_place()`][super::Head::peel_to_id_in_place()] and [`Head::into_fully_peeled_id()`][super::Head::into_fully_peeled_id()].
+ /// The error returned by [`Head::peel_to_id_in_place()`](super::Head::try_peel_to_id_in_place())
+ /// and [`Head::into_fully_peeled_id()`](super::Head::try_into_peeled_id()).
#[derive(Debug, thiserror::Error)]
#[allow(missing_docs)]
pub enum Error {
@@ -19,13 +21,11 @@ mod error {
pub use error::Error;
-use crate::head::Kind;
-
///
-pub mod to_commit {
+pub mod into_id {
use crate::object;
- /// The error returned by [`Head::peel_to_commit_in_place()`][super::Head::peel_to_commit_in_place()].
+ /// The error returned by [`Head::into_peeled_id()`](super::Head::into_peeled_id()).
#[derive(Debug, thiserror::Error)]
#[allow(missing_docs)]
pub enum Error {
@@ -38,86 +38,122 @@ pub mod to_commit {
}
}
+///
+pub mod to_commit {
+ use crate::object;
+
+ /// The error returned by [`Head::peel_to_commit_in_place()`](super::Head::peel_to_commit_in_place()).
+ #[derive(Debug, thiserror::Error)]
+ #[allow(missing_docs)]
+ pub enum Error {
+ #[error(transparent)]
+ PeelToObject(#[from] super::to_object::Error),
+ #[error(transparent)]
+ ObjectKind(#[from] object::try_into::Error),
+ }
+}
+
+///
+pub mod to_object {
+ /// The error returned by [`Head::peel_to_object_in_place()`](super::Head::peel_to_object_in_place()).
+ #[derive(Debug, thiserror::Error)]
+ #[allow(missing_docs)]
+ pub enum Error {
+ #[error(transparent)]
+ Peel(#[from] super::Error),
+ #[error("Branch '{name}' does not have any commits")]
+ Unborn { name: gix_ref::FullName },
+ }
+}
+
impl<'repo> Head<'repo> {
- // TODO: tests
- /// Peel this instance to make obtaining its final target id possible, while returning an error on unborn heads.
- pub fn peeled(mut self) -> Result<Self, Error> {
- self.peel_to_id_in_place().transpose()?;
- Ok(self)
+ /// Peel this instance and consume it to make obtaining its final target id possible, while returning an error on unborn heads.
+ ///
+ /// The final target is obtained by following symbolic references and peeling tags to their final destination, which
+ /// typically is a commit, but can be any object.
+ pub fn into_peeled_id(mut self) -> Result<crate::Id<'repo>, into_id::Error> {
+ self.try_peel_to_id_in_place()?;
+ self.id().ok_or_else(|| match self.kind {
+ Kind::Symbolic(gix_ref::Reference { name, .. }) | Kind::Unborn(name) => into_id::Error::Unborn { name },
+ Kind::Detached { .. } => unreachable!("id can be returned after peeling"),
+ })
+ }
+
+ /// Peel this instance and consume it to make obtaining its final target object possible, while returning an error on unborn heads.
+ ///
+ /// The final target is obtained by following symbolic references and peeling tags to their final destination, which
+ /// typically is a commit, but can be any object as well.
+ pub fn into_peeled_object(mut self) -> Result<crate::Object<'repo>, to_object::Error> {
+ self.peel_to_object_in_place()
+ }
+
+ /// Consume this instance and transform it into the final object that it points to, or `Ok(None)` if the `HEAD`
+ /// reference is yet to be born.
+ ///
+ /// The final target is obtained by following symbolic references and peeling tags to their final destination, which
+ /// typically is a commit, but can be any object.
+ pub fn try_into_peeled_id(mut self) -> Result<Option<crate::Id<'repo>>, Error> {
+ self.try_peel_to_id_in_place()
}
- // TODO: tests
- // TODO: Fix this! It's not consistently peeling tags. The whole peeling business should be reconsidered to do what people usually
- // want which is to peel references, if present, and then peel objects with control over which object type to end at.
- // Finding a good interface for that isn't easy as ideally, it's an iterator that shows the intermediate objects so the user
- // can select which tag of a chain to choose.
/// Follow the symbolic reference of this head until its target object and peel it by following tag objects until there is no
/// more object to follow, and return that object id.
///
- /// Returns `None` if the head is unborn.
- pub fn peel_to_id_in_place(&mut self) -> Option<Result<crate::Id<'repo>, Error>> {
- Some(match &mut self.kind {
- Kind::Unborn(_name) => return None,
+ /// Returns `Ok(None)` if the head is unborn.
+ ///
+ /// The final target is obtained by following symbolic references and peeling tags to their final destination, which
+ /// typically is a commit, but can be any object.
+ pub fn try_peel_to_id_in_place(&mut self) -> Result<Option<crate::Id<'repo>>, Error> {
+ Ok(Some(match &mut self.kind {
+ Kind::Unborn(_name) => return Ok(None),
Kind::Detached {
peeled: Some(peeled), ..
- } => Ok((*peeled).attach(self.repo)),
+ } => (*peeled).attach(self.repo),
Kind::Detached { peeled: None, target } => {
- match target
- .attach(self.repo)
- .object()
- .map_err(Into::into)
- .and_then(|obj| obj.peel_tags_to_end().map_err(Into::into))
- .map(|peeled| peeled.id)
- {
- Ok(peeled) => {
- self.kind = Kind::Detached {
- peeled: Some(peeled),
- target: *target,
- };
- Ok(peeled.attach(self.repo))
+ let id = target.attach(self.repo);
+ if id.header()?.kind() == gix_object::Kind::Commit {
+ id
+ } else {
+ match id.object()?.peel_tags_to_end() {
+ Ok(obj) => {
+ self.kind = Kind::Detached {
+ peeled: Some(obj.id),
+ target: *target,
+ };
+ obj.id()
+ }
+ Err(err) => return Err(err.into()),
}
- Err(err) => Err(err),
}
}
Kind::Symbolic(r) => {
let mut nr = r.clone().attach(self.repo);
- let peeled = nr.peel_to_id_in_place().map_err(Into::into);
+ let peeled = nr.peel_to_id_in_place();
*r = nr.detach();
- peeled
+ peeled?
}
- })
+ }))
}
- // TODO: tests
- // TODO: something similar in `crate::Reference`
/// Follow the symbolic reference of this head until its target object and peel it by following tag objects until there is no
/// more object to follow, transform the id into a commit if possible and return that.
///
/// Returns an error if the head is unborn or if it doesn't point to a commit.
- pub fn peel_to_commit_in_place(&mut self) -> Result<crate::Commit<'repo>, to_commit::Error> {
- let id = self.peel_to_id_in_place().ok_or_else(|| to_commit::Error::Unborn {
- name: self.referent_name().expect("unborn").to_owned(),
- })??;
+ pub fn peel_to_object_in_place(&mut self) -> Result<crate::Object<'repo>, to_object::Error> {
+ let id = self
+ .try_peel_to_id_in_place()?
+ .ok_or_else(|| to_object::Error::Unborn {
+ name: self.referent_name().expect("unborn").to_owned(),
+ })?;
id.object()
- .map_err(|err| to_commit::Error::Peel(Error::FindExistingObject(err)))
- .and_then(|object| object.try_into_commit().map_err(Into::into))
+ .map_err(|err| to_object::Error::Peel(Error::FindExistingObject(err)))
}
- /// Consume this instance and transform it into the final object that it points to, or `None` if the `HEAD`
- /// reference is yet to be born.
- pub fn into_fully_peeled_id(self) -> Option<Result<crate::Id<'repo>, Error>> {
- Some(match self.kind {
- Kind::Unborn(_name) => return None,
- Kind::Detached {
- peeled: Some(peeled), ..
- } => Ok(peeled.attach(self.repo)),
- Kind::Detached { peeled: None, target } => target
- .attach(self.repo)
- .object()
- .map_err(Into::into)
- .and_then(|obj| obj.peel_tags_to_end().map_err(Into::into))
- .map(|obj| obj.id.attach(self.repo)),
- Kind::Symbolic(r) => r.attach(self.repo).peel_to_id_in_place().map_err(Into::into),
- })
+ /// Follow the symbolic reference of this head until its target object and peel it by following tag objects until there is no
+ /// more object to follow, transform the id into a commit if possible and return that.
+ ///
+ /// Returns an error if the head is unborn or if it doesn't point to a commit.
+ pub fn peel_to_commit_in_place(&mut self) -> Result<crate::Commit<'repo>, to_commit::Error> {
+ Ok(self.peel_to_object_in_place()?.try_into_commit()?)
}
}
diff --git a/vendor/gix/src/lib.rs b/vendor/gix/src/lib.rs
index 8218defc8..d4928b9de 100644
--- a/vendor/gix/src/lib.rs
+++ b/vendor/gix/src/lib.rs
@@ -82,10 +82,10 @@
//!
//! ### Feature Flags
#![cfg_attr(
- feature = "document-features",
- cfg_attr(doc, doc = ::document_features::document_features!())
+ all(doc, feature = "document-features"),
+ doc = ::document_features::document_features!()
)]
-#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))]
+#![cfg_attr(all(doc, feature = "document-features"), feature(doc_cfg, doc_auto_cfg))]
#![deny(missing_docs, rust_2018_idioms, unsafe_code)]
#![allow(clippy::result_large_err)]
diff --git a/vendor/gix/src/object/blob.rs b/vendor/gix/src/object/blob.rs
index ef19c302a..59629727f 100644
--- a/vendor/gix/src/object/blob.rs
+++ b/vendor/gix/src/object/blob.rs
@@ -5,61 +5,132 @@ use crate::{Blob, ObjectDetached};
pub mod diff {
use std::ops::Range;
- use crate::{bstr::ByteSlice, object::blob::diff::line::Change};
+ use gix_diff::blob::{platform::prepare_diff::Operation, ResourceKind};
+
+ use crate::{
+ bstr::ByteSlice,
+ object::{blob::diff::lines::Change, tree::diff::change::Event},
+ };
/// A platform to keep temporary information to perform line diffs on modified blobs.
///
- pub struct Platform<'old, 'new> {
- /// The previous version of the blob.
- pub old: crate::Object<'old>,
- /// The new version of the blob.
- pub new: crate::Object<'new>,
- /// The algorithm to use when calling [imara_diff::diff()][gix_diff::blob::diff()].
- /// This value is determined by the `diff.algorithm` configuration.
- pub algo: gix_diff::blob::Algorithm,
+ pub struct Platform<'a> {
+ /// The cache holding diffable data related to our blobs.
+ pub resource_cache: &'a mut gix_diff::blob::Platform,
}
///
pub mod init {
- /// The error returned by [`Platform::from_ids()`][super::Platform::from_ids()].
- #[derive(Debug, thiserror::Error)]
- #[allow(missing_docs)]
- pub enum Error {
- #[error("Could not find the previous blob or the new blob to diff against")]
- FindExisting(#[from] crate::object::find::existing::Error),
- #[error("Could not obtain diff algorithm from configuration")]
- DiffAlgorithm(#[from] crate::config::diff::algorithm::Error),
- }
+ /// The error returned by [`Platform::from_tree_change()`][super::Platform::from_tree_change()].
+ pub type Error = gix_diff::blob::platform::set_resource::Error;
}
- impl<'old, 'new> Platform<'old, 'new> {
- /// Produce a platform for performing various diffs after obtaining the object data of `previous_id` and `new_id`.
- ///
- /// Note that these objects are treated as raw data and are assumed to be blobs.
- pub fn from_ids(
- previous_id: &crate::Id<'old>,
- new_id: &crate::Id<'new>,
- ) -> Result<Platform<'old, 'new>, init::Error> {
- match previous_id
- .object()
- .and_then(|old| new_id.object().map(|new| (old, new)))
- {
- Ok((old, new)) => {
- let algo = match new_id.repo.config.diff_algorithm() {
- Ok(algo) => algo,
- Err(err) => return Err(err.into()),
- };
- Ok(Platform { old, new, algo })
+ impl<'a> Platform<'a> {
+ /// Produce a platform for performing various diffs after obtaining the data from a single `tree_change`.
+ pub fn from_tree_change(
+ tree_change: &crate::object::tree::diff::Change<'_, '_, '_>,
+ resource_cache: &'a mut gix_diff::blob::Platform,
+ ) -> Result<Platform<'a>, init::Error> {
+ match tree_change.event {
+ Event::Addition { entry_mode, id } => {
+ resource_cache.set_resource(
+ id.repo.object_hash().null(),
+ entry_mode.kind(),
+ tree_change.location,
+ ResourceKind::OldOrSource,
+ &id.repo.objects,
+ )?;
+ resource_cache.set_resource(
+ id.inner,
+ entry_mode.kind(),
+ tree_change.location,
+ ResourceKind::NewOrDestination,
+ &id.repo.objects,
+ )?;
+ }
+ Event::Deletion { entry_mode, id } => {
+ resource_cache.set_resource(
+ id.inner,
+ entry_mode.kind(),
+ tree_change.location,
+ ResourceKind::OldOrSource,
+ &id.repo.objects,
+ )?;
+ resource_cache.set_resource(
+ id.repo.object_hash().null(),
+ entry_mode.kind(),
+ tree_change.location,
+ ResourceKind::NewOrDestination,
+ &id.repo.objects,
+ )?;
+ }
+ Event::Modification {
+ previous_entry_mode,
+ previous_id,
+ entry_mode,
+ id,
+ } => {
+ resource_cache.set_resource(
+ previous_id.inner,
+ previous_entry_mode.kind(),
+ tree_change.location,
+ ResourceKind::OldOrSource,
+ &previous_id.repo.objects,
+ )?;
+ resource_cache.set_resource(
+ id.inner,
+ entry_mode.kind(),
+ tree_change.location,
+ ResourceKind::NewOrDestination,
+ &id.repo.objects,
+ )?;
+ }
+ Event::Rewrite {
+ source_location,
+ source_entry_mode,
+ source_id,
+ entry_mode,
+ id,
+ diff: _,
+ copy: _,
+ } => {
+ resource_cache.set_resource(
+ source_id.inner,
+ source_entry_mode.kind(),
+ source_location,
+ ResourceKind::OldOrSource,
+ &source_id.repo.objects,
+ )?;
+ resource_cache.set_resource(
+ id.inner,
+ entry_mode.kind(),
+ tree_change.location,
+ ResourceKind::NewOrDestination,
+ &id.repo.objects,
+ )?;
}
- Err(err) => Err(err.into()),
}
+ Ok(Self { resource_cache })
}
}
///
- pub mod line {
+ pub mod lines {
use crate::bstr::BStr;
+ /// The error returned by [Platform::lines()](super::Platform::lines()).
+ #[derive(Debug, thiserror::Error)]
+ #[allow(missing_docs)]
+ pub enum Error<E>
+ where
+ E: std::error::Error + Send + Sync + 'static,
+ {
+ #[error(transparent)]
+ ProcessHunk(E),
+ #[error(transparent)]
+ PrepareDiff(#[from] gix_diff::blob::platform::prepare_diff::Error),
+ }
+
/// A change to a hunk of lines.
pub enum Change<'a, 'data> {
/// Lines were added.
@@ -82,70 +153,91 @@ pub mod diff {
}
}
- impl<'old, 'new> Platform<'old, 'new> {
+ impl<'a> Platform<'a> {
/// Perform a diff on lines between the old and the new version of a blob, passing each hunk of lines to `process_hunk`.
- /// The diffing algorithm is determined by the `diff.algorithm` configuration.
- ///
- /// Note that you can invoke the diff more flexibly as well.
+ /// The diffing algorithm is determined by the `diff.algorithm` configuration, or individual diff drivers.
+ /// Note that `process_hunk` is not called if one of the involved resources are binary, but that can be determined
+ /// by introspecting the outcome.
// TODO: more tests (only tested insertion right now)
- pub fn lines<FnH, E>(&self, mut process_hunk: FnH) -> Result<(), E>
+ pub fn lines<FnH, E>(
+ &mut self,
+ mut process_hunk: FnH,
+ ) -> Result<gix_diff::blob::platform::prepare_diff::Outcome<'_>, lines::Error<E>>
where
- FnH: FnMut(line::Change<'_, '_>) -> Result<(), E>,
- E: std::error::Error,
+ FnH: FnMut(lines::Change<'_, '_>) -> Result<(), E>,
+ E: std::error::Error + Send + Sync + 'static,
{
- let input = self.line_tokens();
- let mut err = None;
- let mut lines = Vec::new();
- gix_diff::blob::diff(self.algo, &input, |before: Range<u32>, after: Range<u32>| {
- if err.is_some() {
- return;
+ self.resource_cache.options.skip_internal_diff_if_external_is_configured = false;
+
+ let prep = self.resource_cache.prepare_diff()?;
+ match prep.operation {
+ Operation::InternalDiff { algorithm } => {
+ let input = prep.interned_input();
+ let mut err = None;
+ let mut lines = Vec::new();
+
+ gix_diff::blob::diff(algorithm, &input, |before: Range<u32>, after: Range<u32>| {
+ if err.is_some() {
+ return;
+ }
+ lines.clear();
+ lines.extend(
+ input.before[before.start as usize..before.end as usize]
+ .iter()
+ .map(|&line| input.interner[line].as_bstr()),
+ );
+ let end_of_before = lines.len();
+ lines.extend(
+ input.after[after.start as usize..after.end as usize]
+ .iter()
+ .map(|&line| input.interner[line].as_bstr()),
+ );
+ let hunk_before = &lines[..end_of_before];
+ let hunk_after = &lines[end_of_before..];
+ if hunk_after.is_empty() {
+ err = process_hunk(Change::Deletion { lines: hunk_before }).err();
+ } else if hunk_before.is_empty() {
+ err = process_hunk(Change::Addition { lines: hunk_after }).err();
+ } else {
+ err = process_hunk(Change::Modification {
+ lines_before: hunk_before,
+ lines_after: hunk_after,
+ })
+ .err();
+ }
+ });
+
+ if let Some(err) = err {
+ return Err(lines::Error::ProcessHunk(err));
+ }
}
- lines.clear();
- lines.extend(
- input.before[before.start as usize..before.end as usize]
- .iter()
- .map(|&line| input.interner[line].as_bstr()),
- );
- let end_of_before = lines.len();
- lines.extend(
- input.after[after.start as usize..after.end as usize]
- .iter()
- .map(|&line| input.interner[line].as_bstr()),
- );
- let hunk_before = &lines[..end_of_before];
- let hunk_after = &lines[end_of_before..];
- if hunk_after.is_empty() {
- err = process_hunk(Change::Deletion { lines: hunk_before }).err();
- } else if hunk_before.is_empty() {
- err = process_hunk(Change::Addition { lines: hunk_after }).err();
- } else {
- err = process_hunk(Change::Modification {
- lines_before: hunk_before,
- lines_after: hunk_after,
- })
- .err();
+ Operation::ExternalCommand { .. } => {
+ unreachable!("we disabled that")
}
- });
-
- match err {
- Some(err) => Err(err),
- None => Ok(()),
- }
+ Operation::SourceOrDestinationIsBinary => {}
+ };
+ Ok(prep)
}
/// Count the amount of removed and inserted lines efficiently.
- pub fn line_counts(&self) -> gix_diff::blob::sink::Counter<()> {
- let tokens = self.line_tokens();
- gix_diff::blob::diff(self.algo, &tokens, gix_diff::blob::sink::Counter::default())
- }
+ /// Note that nothing will happen if one of the inputs is binary, and `None` will be returned.
+ pub fn line_counts(
+ &mut self,
+ ) -> Result<Option<gix_diff::blob::sink::Counter<()>>, gix_diff::blob::platform::prepare_diff::Error> {
+ self.resource_cache.options.skip_internal_diff_if_external_is_configured = false;
- /// Return a tokenizer which treats lines as smallest unit for use in a [diff operation][gix_diff::blob::diff()].
- ///
- /// The line separator is determined according to normal git rules and filters.
- pub fn line_tokens(&self) -> gix_diff::blob::intern::InternedInput<&[u8]> {
- // TODO: make use of `core.eol` and/or filters to do line-counting correctly. It's probably
- // OK to just know how these objects are saved to know what constitutes a line.
- gix_diff::blob::intern::InternedInput::new(self.old.data.as_bytes(), self.new.data.as_bytes())
+ let prep = self.resource_cache.prepare_diff()?;
+ match prep.operation {
+ Operation::InternalDiff { algorithm } => {
+ let tokens = prep.interned_input();
+ let counter = gix_diff::blob::diff(algorithm, &tokens, gix_diff::blob::sink::Counter::default());
+ Ok(Some(counter))
+ }
+ Operation::ExternalCommand { .. } => {
+ unreachable!("we disabled that")
+ }
+ Operation::SourceOrDestinationIsBinary => Ok(None),
+ }
}
}
}
diff --git a/vendor/gix/src/object/errors.rs b/vendor/gix/src/object/errors.rs
index 92789b6cb..db81daacb 100644
--- a/vendor/gix/src/object/errors.rs
+++ b/vendor/gix/src/object/errors.rs
@@ -20,12 +20,12 @@ pub mod find {
/// Indicate that an error occurred when trying to find an object.
#[derive(Debug, thiserror::Error)]
#[error(transparent)]
- pub struct Error(#[from] pub gix_odb::find::Error);
+ pub struct Error(#[from] pub gix_object::find::Error);
///
pub mod existing {
/// An object could not be found in the database, or an error occurred when trying to obtain it.
- pub type Error = gix_odb::find::existing::Error;
+ pub type Error = gix_object::find::existing::Error;
}
}
@@ -34,5 +34,5 @@ pub mod write {
/// An error to indicate writing to the loose object store failed.
#[derive(Debug, thiserror::Error)]
#[error(transparent)]
- pub struct Error(#[from] pub gix_odb::find::Error);
+ pub struct Error(#[from] pub gix_object::find::Error);
}
diff --git a/vendor/gix/src/object/tree/diff/change.rs b/vendor/gix/src/object/tree/diff/change.rs
index e6826d6ed..e4eb5f3df 100644
--- a/vendor/gix/src/object/tree/diff/change.rs
+++ b/vendor/gix/src/object/tree/diff/change.rs
@@ -1,17 +1,4 @@
-use crate::{bstr::BStr, Id};
-
-/// Information about the diff performed to detect similarity of a [Rewrite][Event::Rewrite].
-#[derive(Debug, Default, Clone, Copy, Eq, PartialEq)]
-pub struct DiffLineStats {
- /// The amount of lines to remove from the source to get to the destination.
- pub removals: u32,
- /// The amount of lines to add to the source to get to the destination.
- pub insertions: u32,
- /// The amount of lines of the previous state, in the source.
- pub before: u32,
- /// The amount of lines of the new state, in the destination.
- pub after: u32,
-}
+use crate::{bstr::BStr, diff::blob::DiffLineStats, Id};
/// An event emitted when finding differences between two trees.
#[derive(Debug, Clone, Copy)]
@@ -80,25 +67,25 @@ pub enum Event<'a, 'old, 'new> {
},
}
-impl<'a, 'old, 'new> Event<'a, 'old, 'new> {
- /// Produce a platform for performing a line-diff, or `None` if this is not a [`Modification`][Event::Modification]
- /// or one of the entries to compare is not a blob.
- pub fn diff(
+impl<'a, 'old, 'new> super::Change<'a, 'old, 'new> {
+ /// Produce a platform for performing a line-diff no matter whether the underlying [Event] is an addition, modification,
+ /// deletion or rewrite.
+ /// Use `resource_cache` to store the diffable data and possibly reuse previously stored data.
+ /// Afterwards the platform, which holds on to `resource_cache`, can be used to perform ready-made operations on the
+ /// pre-set resources.
+ ///
+ /// ### Warning about Memory Consumption
+ ///
+ /// `resource_cache` only grows, so one should call [`gix_diff::blob::Platform::clear_resource_cache`] occasionally.
+ pub fn diff<'b>(
&self,
- ) -> Option<Result<crate::object::blob::diff::Platform<'old, 'new>, crate::object::blob::diff::init::Error>> {
- match self {
- Event::Modification {
- previous_entry_mode,
- previous_id,
- entry_mode,
- id,
- } if entry_mode.is_blob() && previous_entry_mode.is_blob() => {
- Some(crate::object::blob::diff::Platform::from_ids(previous_id, id))
- }
- _ => None,
- }
+ resource_cache: &'b mut gix_diff::blob::Platform,
+ ) -> Result<crate::object::blob::diff::Platform<'b>, crate::object::blob::diff::init::Error> {
+ crate::object::blob::diff::Platform::from_tree_change(self, resource_cache)
}
+}
+impl<'a, 'old, 'new> Event<'a, 'old, 'new> {
/// Return the current mode of this instance.
pub fn entry_mode(&self) -> gix_object::tree::EntryMode {
match self {
diff --git a/vendor/gix/src/object/tree/diff/for_each.rs b/vendor/gix/src/object/tree/diff/for_each.rs
index 3932f9027..c1b76fbe6 100644
--- a/vendor/gix/src/object/tree/diff/for_each.rs
+++ b/vendor/gix/src/object/tree/diff/for_each.rs
@@ -1,14 +1,11 @@
use gix_object::TreeRefIter;
-use gix_odb::FindExt;
use super::{change, Action, Change, Platform};
use crate::{
bstr::BStr,
+ diff::{rewrites, rewrites::tracker},
ext::ObjectIdExt,
- object::tree::{
- diff,
- diff::{rewrites, tracked},
- },
+ object::tree::diff,
Repository, Tree,
};
@@ -20,12 +17,10 @@ pub enum Error {
Diff(#[from] gix_diff::tree::changes::Error),
#[error("The user-provided callback failed")]
ForEach(#[source] Box<dyn std::error::Error + Send + Sync + 'static>),
- #[error("Could not find blob for similarity checking")]
- FindExistingBlob(#[from] crate::object::find::existing::Error),
- #[error("Could not configure diff algorithm prior to checking similarity")]
- ConfigureDiffAlgorithm(#[from] crate::config::diff::algorithm::Error),
- #[error("Could not traverse tree to obtain possible sources for copies")]
- TraverseTreeForExhaustiveCopyDetection(#[from] gix_traverse::tree::breadthfirst::Error),
+ #[error(transparent)]
+ ResourceCache(#[from] crate::repository::diff::resource_cache::Error),
+ #[error("Failure during rename tracking")]
+ RenameTracking(#[from] tracker::emit::Error),
}
///
@@ -49,24 +44,59 @@ impl<'a, 'old> Platform<'a, 'old> {
where
E: std::error::Error + Sync + Send + 'static,
{
+ self.for_each_to_obtain_tree_inner(other, for_each, None)
+ }
+
+ /// Like [`Self::for_each_to_obtain_tree()`], but with a reusable `resource_cache` which is used to perform
+ /// diffs fast.
+ ///
+ /// Reusing it between multiple invocations saves a lot of IOps as it avoids the creation
+ /// of a temporary `resource_cache` that triggers reading or checking for multiple gitattribute files.
+ /// Note that it's recommended to call [`gix_diff::blob::Platform::clear_resource_cache()`] between the calls
+ /// to avoid runaway memory usage, as the cache isn't limited.
+ ///
+ /// Note that to do rename tracking like `git` does, one has to configure the `resource_cache` with
+ /// a conversion pipeline that uses [`gix_diff::blob::pipeline::Mode::ToGit`].
+ pub fn for_each_to_obtain_tree_with_cache<'new, E>(
+ &mut self,
+ other: &Tree<'new>,
+ resource_cache: &mut gix_diff::blob::Platform,
+ for_each: impl FnMut(Change<'_, 'old, 'new>) -> Result<Action, E>,
+ ) -> Result<Outcome, Error>
+ where
+ E: std::error::Error + Sync + Send + 'static,
+ {
+ self.for_each_to_obtain_tree_inner(other, for_each, Some(resource_cache))
+ }
+
+ fn for_each_to_obtain_tree_inner<'new, E>(
+ &mut self,
+ other: &Tree<'new>,
+ for_each: impl FnMut(Change<'_, 'old, 'new>) -> Result<Action, E>,
+ resource_cache: Option<&mut gix_diff::blob::Platform>,
+ ) -> Result<Outcome, Error>
+ where
+ E: std::error::Error + Sync + Send + 'static,
+ {
let repo = self.lhs.repo;
let mut delegate = Delegate {
src_tree: self.lhs,
other_repo: other.repo,
recorder: gix_diff::tree::Recorder::default().track_location(self.tracking),
visit: for_each,
- tracked: self.rewrites.map(|r| tracked::State::new(r, self.tracking)),
+ location: self.tracking,
+ tracked: self.rewrites.map(rewrites::Tracker::new),
err: None,
};
match gix_diff::tree::Changes::from(TreeRefIter::from_bytes(&self.lhs.data)).needed_to_obtain(
TreeRefIter::from_bytes(&other.data),
&mut self.state,
- |oid, buf| repo.objects.find_tree_iter(oid, buf),
+ &repo.objects,
&mut delegate,
) {
Ok(()) => {
let outcome = Outcome {
- rewrites: delegate.process_tracked_changes()?,
+ rewrites: delegate.process_tracked_changes(resource_cache)?,
};
match delegate.err {
Some(err) => Err(Error::ForEach(Box::new(err))),
@@ -88,7 +118,8 @@ struct Delegate<'a, 'old, 'new, VisitFn, E> {
other_repo: &'new Repository,
recorder: gix_diff::tree::Recorder,
visit: VisitFn,
- tracked: Option<tracked::State>,
+ tracked: Option<rewrites::Tracker<gix_diff::tree::visit::Change>>,
+ location: Option<gix_diff::tree::recorder::Location>,
err: Option<E>,
}
@@ -138,12 +169,25 @@ where
}
}
- fn process_tracked_changes(&mut self) -> Result<Option<rewrites::Outcome>, Error> {
+ fn process_tracked_changes(
+ &mut self,
+ diff_cache: Option<&mut gix_diff::blob::Platform>,
+ ) -> Result<Option<rewrites::Outcome>, Error> {
let tracked = match self.tracked.as_mut() {
Some(t) => t,
None => return Ok(None),
};
+ let repo = self.src_tree.repo;
+ let mut storage;
+ let diff_cache = match diff_cache {
+ Some(cache) => cache,
+ None => {
+ storage = repo.diff_resource_cache(gix_diff::blob::pipeline::Mode::ToGit, Default::default())?;
+ &mut storage
+ }
+ };
+
let outcome = tracked.emit(
|dest, source| match source {
Some(source) => {
@@ -152,14 +196,14 @@ where
location: dest.location,
event: diff::change::Event::Rewrite {
source_location: source.location,
- source_entry_mode: source.mode,
+ source_entry_mode: source.entry_mode,
source_id: source.id.attach(self.src_tree.repo),
entry_mode: mode,
id: oid.to_owned().attach(self.other_repo),
diff: source.diff,
copy: match source.kind {
- tracked::visit::Kind::RenameTarget => false,
- tracked::visit::Kind::CopyDestination => true,
+ tracker::visit::SourceKind::Rename => false,
+ tracker::visit::SourceKind::Copy => true,
},
},
};
@@ -181,7 +225,13 @@ where
&mut self.err,
),
},
- self.src_tree,
+ diff_cache,
+ &self.src_tree.repo.objects,
+ |push| {
+ self.src_tree
+ .traverse()
+ .breadthfirst(&mut tree_to_changes::Delegate::new(push, self.location))
+ },
)?;
Ok(Some(outcome))
}
@@ -234,3 +284,68 @@ where
}
}
}
+
+mod tree_to_changes {
+ use gix_diff::tree::visit::Change;
+ use gix_object::tree::EntryRef;
+
+ use crate::bstr::BStr;
+
+ pub struct Delegate<'a> {
+ push: &'a mut dyn FnMut(Change, &BStr),
+ recorder: gix_traverse::tree::Recorder,
+ }
+
+ impl<'a> Delegate<'a> {
+ pub fn new(
+ push: &'a mut dyn FnMut(Change, &BStr),
+ location: Option<gix_diff::tree::recorder::Location>,
+ ) -> Self {
+ let location = location.map(|t| match t {
+ gix_diff::tree::recorder::Location::FileName => gix_traverse::tree::recorder::Location::FileName,
+ gix_diff::tree::recorder::Location::Path => gix_traverse::tree::recorder::Location::Path,
+ });
+ Self {
+ push,
+ recorder: gix_traverse::tree::Recorder::default().track_location(location),
+ }
+ }
+ }
+
+ impl gix_traverse::tree::Visit for Delegate<'_> {
+ fn pop_front_tracked_path_and_set_current(&mut self) {
+ self.recorder.pop_front_tracked_path_and_set_current()
+ }
+
+ fn push_back_tracked_path_component(&mut self, component: &BStr) {
+ self.recorder.push_back_tracked_path_component(component)
+ }
+
+ fn push_path_component(&mut self, component: &BStr) {
+ self.recorder.push_path_component(component)
+ }
+
+ fn pop_path_component(&mut self) {
+ self.recorder.pop_path_component();
+ }
+
+ fn visit_tree(&mut self, _entry: &EntryRef<'_>) -> gix_traverse::tree::visit::Action {
+ gix_traverse::tree::visit::Action::Continue
+ }
+
+ fn visit_nontree(&mut self, entry: &EntryRef<'_>) -> gix_traverse::tree::visit::Action {
+ if entry.mode.is_blob() {
+ (self.push)(
+ Change::Modification {
+ previous_entry_mode: entry.mode,
+ previous_oid: gix_hash::ObjectId::null(entry.oid.kind()),
+ entry_mode: entry.mode,
+ oid: entry.oid.to_owned(),
+ },
+ self.recorder.path(),
+ );
+ }
+ gix_traverse::tree::visit::Action::Continue
+ }
+ }
+}
diff --git a/vendor/gix/src/object/tree/diff/mod.rs b/vendor/gix/src/object/tree/diff/mod.rs
index 5f7a041e4..858775610 100644
--- a/vendor/gix/src/object/tree/diff/mod.rs
+++ b/vendor/gix/src/object/tree/diff/mod.rs
@@ -1,6 +1,6 @@
use gix_diff::tree::recorder::Location;
-use crate::{bstr::BStr, Tree};
+use crate::{bstr::BStr, diff::Rewrites, Tree};
/// Returned by the `for_each` function to control flow.
#[derive(Default, Clone, Copy, PartialOrd, PartialEq, Ord, Eq, Hash)]
@@ -39,7 +39,7 @@ impl<'repo> Tree<'repo> {
/// try to access blobs to compute a similarity metric. Thus, it's more compatible to turn rewrite tracking off
/// using [`Platform::track_rewrites()`].
#[allow(clippy::result_large_err)]
- pub fn changes<'a>(&'a self) -> Result<Platform<'a, 'repo>, rewrites::Error> {
+ pub fn changes<'a>(&'a self) -> Result<Platform<'a, 'repo>, crate::diff::new_rewrites::Error> {
Ok(Platform {
state: Default::default(),
lhs: self,
@@ -58,34 +58,6 @@ pub struct Platform<'a, 'repo> {
rewrites: Option<Rewrites>,
}
-/// A structure to capture how to perform rename and copy tracking
-#[derive(Debug, Copy, Clone, PartialEq)]
-pub struct Rewrites {
- /// If `Some(…)`, do also find copies. `None` is the default which does not try to detect copies at all.
- ///
- /// Note that this is an even more expensive operation than detecting renames as files.
- pub copies: Option<rewrites::Copies>,
- /// The percentage of similarity needed for files to be considered renamed, defaulting to `Some(0.5)`.
- /// This field is similar to `git diff -M50%`.
- ///
- /// If `None`, files are only considered equal if their content matches 100%.
- /// Note that values greater than 1.0 have no different effect than 1.0.
- pub percentage: Option<f32>,
- /// The amount of files to consider for fuzzy rename or copy tracking. Defaults to 1000, meaning that only 1000*1000
- /// combinations can be tested for fuzzy matches, i.e. the ones that try to find matches by comparing similarity.
- /// If 0, there is no limit.
- ///
- /// If the limit would not be enough to test the entire set of combinations, the algorithm will trade in precision and not
- /// run the fuzzy version of identity tests at all. That way results are never partial.
- pub limit: usize,
-}
-
-///
-pub mod rewrites;
-
-/// types to actually perform rename tracking.
-pub(crate) mod tracked;
-
/// Configuration
impl<'a, 'repo> Platform<'a, 'repo> {
/// Keep track of file-names, which makes the [`location`][Change::location] field usable with the filename of the changed item.
diff --git a/vendor/gix/src/object/tree/diff/rewrites.rs b/vendor/gix/src/object/tree/diff/rewrites.rs
deleted file mode 100644
index e434726d9..000000000
--- a/vendor/gix/src/object/tree/diff/rewrites.rs
+++ /dev/null
@@ -1,108 +0,0 @@
-use crate::{
- config::{cache::util::ApplyLeniency, tree::Diff},
- diff::rename::Tracking,
- object::tree::diff::Rewrites,
-};
-
-/// From where to source copies
-#[derive(Debug, Copy, Clone, Eq, PartialEq)]
-pub enum CopySource {
- /// Find copies from the set of modified files only.
- FromSetOfModifiedFiles,
- /// Find copies from the set of changed files, as well as all files known to the source (i.e. previous state) of the tree.
- ///
- /// This can be an expensive operation as it scales exponentially with the total amount of files in the tree.
- FromSetOfModifiedFilesAndSourceTree,
-}
-
-/// How to determine copied files.
-#[derive(Debug, Copy, Clone, PartialEq)]
-pub struct Copies {
- /// The set of files to search when finding the source of copies.
- pub source: CopySource,
- /// Equivalent to [`Rewrites::percentage`], but used for copy tracking.
- ///
- /// Useful to have similarity-based rename tracking and cheaper copy tracking, which also is the default
- /// as only identity plays a role.
- pub percentage: Option<f32>,
-}
-
-impl Default for Copies {
- fn default() -> Self {
- Copies {
- source: CopySource::FromSetOfModifiedFiles,
- percentage: Some(0.5),
- }
- }
-}
-
-/// Information collected while handling rewrites of files which may be tracked.
-#[derive(Default, Clone, Copy, Debug, PartialEq)]
-pub struct Outcome {
- /// The options used to guide the rewrite tracking. Either fully provided by the caller or retrieved from git configuration.
- pub options: Rewrites,
- /// The amount of similarity checks that have been conducted to find renamed files and potentially copies.
- pub num_similarity_checks: usize,
- /// Set to the amount of worst-case rename permutations we didn't search as our limit didn't allow it.
- pub num_similarity_checks_skipped_for_rename_tracking_due_to_limit: usize,
- /// Set to the amount of worst-case copy permutations we didn't search as our limit didn't allow it.
- pub num_similarity_checks_skipped_for_copy_tracking_due_to_limit: usize,
-}
-
-/// The error returned by [`Rewrites::try_from_config()`].
-#[derive(Debug, thiserror::Error)]
-#[allow(missing_docs)]
-pub enum Error {
- #[error(transparent)]
- ConfigDiffRenames(#[from] crate::config::key::GenericError),
- #[error(transparent)]
- ConfigDiffRenameLimit(#[from] crate::config::unsigned_integer::Error),
-}
-
-/// The default settings for rewrites according to the git configuration defaults.
-impl Default for Rewrites {
- fn default() -> Self {
- Rewrites {
- copies: None,
- percentage: Some(0.5),
- limit: 1000,
- }
- }
-}
-
-impl Rewrites {
- /// Create an instance by reading all relevant information from the `config`uration, while being `lenient` or not.
- /// Returns `Ok(None)` if nothing is configured.
- ///
- /// Note that missing values will be defaulted similar to what git does.
- #[allow(clippy::result_large_err)]
- pub fn try_from_config(config: &gix_config::File<'static>, lenient: bool) -> Result<Option<Self>, Error> {
- let key = "diff.renames";
- let copies = match config
- .boolean_by_key(key)
- .map(|value| Diff::RENAMES.try_into_renames(value))
- .transpose()
- .with_leniency(lenient)?
- {
- Some(renames) => match renames {
- Tracking::Disabled => return Ok(None),
- Tracking::Renames => None,
- Tracking::RenamesAndCopies => Some(Copies::default()),
- },
- None => return Ok(None),
- };
-
- let default = Self::default();
- Ok(Rewrites {
- copies,
- limit: config
- .integer_by_key("diff.renameLimit")
- .map(|value| Diff::RENAME_LIMIT.try_into_usize(value))
- .transpose()
- .with_leniency(lenient)?
- .unwrap_or(default.limit),
- ..default
- }
- .into())
- }
-}
diff --git a/vendor/gix/src/object/tree/diff/tracked.rs b/vendor/gix/src/object/tree/diff/tracked.rs
deleted file mode 100644
index d6042fcbc..000000000
--- a/vendor/gix/src/object/tree/diff/tracked.rs
+++ /dev/null
@@ -1,490 +0,0 @@
-use std::ops::Range;
-
-use gix_diff::tree::visit::Change;
-use gix_object::tree::EntryMode;
-
-use crate::{
- bstr::BStr,
- ext::ObjectIdExt,
- object::tree::diff::{
- change::DiffLineStats,
- rewrites::{CopySource, Outcome},
- Rewrites,
- },
- Repository, Tree,
-};
-
-/// A set of tracked items allows to figure out their relations by figuring out their similarity.
-pub struct Item {
- /// The underlying raw change
- change: Change,
- /// That slice into the backing for paths.
- location: Range<usize>,
- /// If true, this item was already emitted, i.e. seen by the caller.
- emitted: bool,
-}
-
-impl Item {
- fn location<'a>(&self, backing: &'a [u8]) -> &'a BStr {
- backing[self.location.clone()].as_ref()
- }
- fn entry_mode_compatible(&self, mode: EntryMode) -> bool {
- use EntryMode::*;
- matches!(
- (mode, self.change.entry_mode()),
- (Blob | BlobExecutable, Blob | BlobExecutable) | (Link, Link)
- )
- }
-
- fn is_source_for_destination_of(&self, kind: visit::Kind, dest_item_mode: EntryMode) -> bool {
- self.entry_mode_compatible(dest_item_mode)
- && match kind {
- visit::Kind::RenameTarget => !self.emitted && matches!(self.change, Change::Deletion { .. }),
- visit::Kind::CopyDestination => {
- matches!(self.change, Change::Modification { .. })
- }
- }
- }
-}
-
-pub struct State {
- items: Vec<Item>,
- path_backing: Vec<u8>,
- rewrites: Rewrites,
- tracking: Option<gix_diff::tree::recorder::Location>,
-}
-
-pub mod visit {
- use crate::{bstr::BStr, object::tree::diff::change::DiffLineStats};
-
- pub struct Source<'a> {
- pub mode: gix_object::tree::EntryMode,
- pub id: gix_hash::ObjectId,
- pub kind: Kind,
- pub location: &'a BStr,
- pub diff: Option<DiffLineStats>,
- }
-
- #[derive(Debug, Copy, Clone, Eq, PartialEq)]
- pub enum Kind {
- RenameTarget,
- CopyDestination,
- }
-
- pub struct Destination<'a> {
- pub change: gix_diff::tree::visit::Change,
- pub location: &'a BStr,
- }
-}
-
-impl State {
- pub(crate) fn new(renames: Rewrites, tracking: Option<gix_diff::tree::recorder::Location>) -> Self {
- State {
- items: vec![],
- path_backing: vec![],
- rewrites: renames,
- tracking,
- }
- }
-}
-
-/// build state and find matches.
-impl State {
- /// We may refuse the push if that information isn't needed for what we have to track.
- pub fn try_push_change(&mut self, change: Change, location: &BStr) -> Option<Change> {
- if !change.entry_mode().is_blob_or_symlink() {
- return Some(change);
- }
- let keep = match (self.rewrites.copies, &change) {
- (Some(_find_copies), _) => true,
- (None, Change::Modification { .. }) => false,
- (None, _) => true,
- };
-
- if !keep {
- return Some(change);
- }
-
- let start = self.path_backing.len();
- self.path_backing.extend_from_slice(location);
- self.items.push(Item {
- location: start..self.path_backing.len(),
- change,
- emitted: false,
- });
- None
- }
-
- /// Can only be called once effectively as it alters its own state.
- ///
- /// `cb(destination, source)` is called for each item, either with `Some(source)` if it's
- /// the destination of a copy or rename, or with `None` for source if no relation to other
- /// items in the tracked set exist.
- pub fn emit(
- &mut self,
- mut cb: impl FnMut(visit::Destination<'_>, Option<visit::Source<'_>>) -> gix_diff::tree::visit::Action,
- src_tree: &Tree<'_>,
- ) -> Result<Outcome, crate::object::tree::diff::for_each::Error> {
- fn by_id_and_location(a: &Item, b: &Item) -> std::cmp::Ordering {
- a.change.oid().cmp(b.change.oid()).then_with(|| {
- a.location
- .start
- .cmp(&b.location.start)
- .then(a.location.end.cmp(&b.location.end))
- })
- }
- self.items.sort_by(by_id_and_location);
-
- let mut out = Outcome {
- options: self.rewrites,
- ..Default::default()
- };
- out = self.match_pairs_of_kind(
- visit::Kind::RenameTarget,
- &mut cb,
- self.rewrites.percentage,
- out,
- src_tree.repo,
- )?;
-
- if let Some(copies) = self.rewrites.copies {
- out = self.match_pairs_of_kind(
- visit::Kind::CopyDestination,
- &mut cb,
- copies.percentage,
- out,
- src_tree.repo,
- )?;
-
- match copies.source {
- CopySource::FromSetOfModifiedFiles => {}
- CopySource::FromSetOfModifiedFilesAndSourceTree => {
- src_tree
- .traverse()
- .breadthfirst(&mut tree_to_events::Delegate::new(self))?;
- self.items.sort_by(by_id_and_location);
-
- out = self.match_pairs_of_kind(
- visit::Kind::CopyDestination,
- &mut cb,
- copies.percentage,
- out,
- src_tree.repo,
- )?;
- }
- }
- }
-
- self.items
- .sort_by(|a, b| a.location(&self.path_backing).cmp(b.location(&self.path_backing)));
- for item in self.items.drain(..).filter(|item| !item.emitted) {
- if cb(
- visit::Destination {
- location: item.location(&self.path_backing),
- change: item.change,
- },
- None,
- ) == gix_diff::tree::visit::Action::Cancel
- {
- break;
- }
- }
- Ok(out)
- }
-
- fn match_pairs_of_kind(
- &mut self,
- kind: visit::Kind,
- cb: &mut impl FnMut(visit::Destination<'_>, Option<visit::Source<'_>>) -> gix_diff::tree::visit::Action,
- percentage: Option<f32>,
- mut out: Outcome,
- repo: &Repository,
- ) -> Result<Outcome, crate::object::tree::diff::for_each::Error> {
- // we try to cheaply reduce the set of possibilities first, before possibly looking more exhaustively.
- let needs_second_pass = !needs_exact_match(percentage);
- if self.match_pairs(cb, None /* by identity */, kind, repo, &mut out)? == gix_diff::tree::visit::Action::Cancel
- {
- return Ok(out);
- }
- if needs_second_pass {
- let is_limited = if self.rewrites.limit == 0 {
- false
- } else if let Some(permutations) = permutations_over_limit(&self.items, self.rewrites.limit, kind) {
- match kind {
- visit::Kind::RenameTarget => {
- out.num_similarity_checks_skipped_for_rename_tracking_due_to_limit = permutations;
- }
- visit::Kind::CopyDestination => {
- out.num_similarity_checks_skipped_for_copy_tracking_due_to_limit = permutations;
- }
- }
- true
- } else {
- false
- };
- if !is_limited {
- self.match_pairs(cb, self.rewrites.percentage, kind, repo, &mut out)?;
- }
- }
- Ok(out)
- }
-
- fn match_pairs(
- &mut self,
- cb: &mut impl FnMut(visit::Destination<'_>, Option<visit::Source<'_>>) -> gix_diff::tree::visit::Action,
- percentage: Option<f32>,
- kind: visit::Kind,
- repo: &Repository,
- stats: &mut Outcome,
- ) -> Result<gix_diff::tree::visit::Action, crate::object::tree::diff::for_each::Error> {
- // TODO(perf): reuse object data and interner state and interned tokens, make these available to `find_match()`
- let mut dest_ofs = 0;
- while let Some((mut dest_idx, dest)) = self.items[dest_ofs..].iter().enumerate().find_map(|(idx, item)| {
- (!item.emitted && matches!(item.change, Change::Addition { .. })).then_some((idx, item))
- }) {
- dest_idx += dest_ofs;
- dest_ofs = dest_idx + 1;
- let src =
- find_match(&self.items, dest, dest_idx, percentage, kind, repo, stats)?.map(|(src_idx, src, diff)| {
- let (id, mode) = src.change.oid_and_entry_mode();
- let id = id.to_owned();
- let location = src.location(&self.path_backing);
- (
- visit::Source {
- mode,
- id,
- kind,
- location,
- diff,
- },
- src_idx,
- )
- });
- if src.is_none() {
- continue;
- }
- let location = dest.location(&self.path_backing);
- let change = dest.change.clone();
- let dest = visit::Destination { change, location };
- self.items[dest_idx].emitted = true;
- if let Some(src_idx) = src.as_ref().map(|t| t.1) {
- self.items[src_idx].emitted = true;
- }
- if cb(dest, src.map(|t| t.0)) == gix_diff::tree::visit::Action::Cancel {
- return Ok(gix_diff::tree::visit::Action::Cancel);
- }
- }
- Ok(gix_diff::tree::visit::Action::Continue)
- }
-}
-
-fn permutations_over_limit(items: &[Item], limit: usize, kind: visit::Kind) -> Option<usize> {
- let (sources, destinations) = items
- .iter()
- .filter(|item| match kind {
- visit::Kind::RenameTarget => !item.emitted,
- visit::Kind::CopyDestination => true,
- })
- .fold((0, 0), |(mut src, mut dest), item| {
- match item.change {
- Change::Addition { .. } => {
- dest += 1;
- }
- Change::Deletion { .. } => {
- if kind == visit::Kind::RenameTarget {
- src += 1
- }
- }
- Change::Modification { .. } => {
- if kind == visit::Kind::CopyDestination {
- src += 1
- }
- }
- }
- (src, dest)
- });
- let permutations = sources * destinations;
- (permutations > limit * limit).then_some(permutations)
-}
-
-fn needs_exact_match(percentage: Option<f32>) -> bool {
- percentage.map_or(true, |p| p >= 1.0)
-}
-
-/// <`src_idx`, src, possibly diff stat>
-type SourceTuple<'a> = (usize, &'a Item, Option<DiffLineStats>);
-
-/// Find `item` in our set of items ignoring `item_idx` to avoid finding ourselves, by similarity indicated by `percentage`.
-/// The latter can be `None` or `Some(x)` where `x>=1` for identity, and anything else for similarity.
-/// We also ignore emitted items entirely.
-/// Use `kind` to indicate what kind of match we are looking for, which might be deletions matching an `item` addition, or
-/// any non-deletion otherwise.
-/// Note that we always try to find by identity first even if a percentage is given as it's much faster and may reduce the set
-/// of items to be searched.
-fn find_match<'a>(
- items: &'a [Item],
- item: &Item,
- item_idx: usize,
- percentage: Option<f32>,
- kind: visit::Kind,
- repo: &Repository,
- stats: &mut Outcome,
-) -> Result<Option<SourceTuple<'a>>, crate::object::tree::diff::for_each::Error> {
- let (item_id, item_mode) = item.change.oid_and_entry_mode();
- if needs_exact_match(percentage) || item_mode == gix_object::tree::EntryMode::Link {
- let first_idx = items.partition_point(|a| a.change.oid() < item_id);
- let range = match items.get(first_idx..).map(|items| {
- let end = items
- .iter()
- .position(|a| a.change.oid() != item_id)
- .map_or(items.len(), |idx| first_idx + idx);
- first_idx..end
- }) {
- Some(range) => range,
- None => return Ok(None),
- };
- if range.is_empty() {
- return Ok(None);
- }
- let res = items[range.clone()].iter().enumerate().find_map(|(mut src_idx, src)| {
- src_idx += range.start;
- (src_idx != item_idx && src.is_source_for_destination_of(kind, item_mode)).then_some((src_idx, src, None))
- });
- if let Some(src) = res {
- return Ok(Some(src));
- }
- } else {
- let new = item_id.to_owned().attach(repo).object()?;
- let percentage = percentage.expect("it's set to something below 1.0 and we assured this");
- debug_assert!(
- item.change.entry_mode().is_blob(),
- "symlinks are matched exactly, and trees aren't used here"
- );
- let algo = repo.config.diff_algorithm()?;
- for (can_idx, src) in items
- .iter()
- .enumerate()
- .filter(|(src_idx, src)| *src_idx != item_idx && src.is_source_for_destination_of(kind, item_mode))
- {
- let old = src.change.oid().to_owned().attach(repo).object()?;
- // TODO: make sure we get attribute handling and binary skips and filters right here. There is crate::object::blob::diff::Platform
- // which should have facilities for that one day, but we don't use it because we need newlines in our tokens.
- let tokens = gix_diff::blob::intern::InternedInput::new(
- gix_diff::blob::sources::byte_lines_with_terminator(&old.data),
- gix_diff::blob::sources::byte_lines_with_terminator(&new.data),
- );
- let counts = gix_diff::blob::diff(
- algo,
- &tokens,
- gix_diff::blob::sink::Counter::new(diff::Statistics {
- removed_bytes: 0,
- input: &tokens,
- }),
- );
- let similarity = (old.data.len() - counts.wrapped) as f32 / old.data.len().max(new.data.len()) as f32;
- stats.num_similarity_checks += 1;
- if similarity >= percentage {
- return Ok(Some((
- can_idx,
- src,
- DiffLineStats {
- removals: counts.removals,
- insertions: counts.insertions,
- before: tokens.before.len().try_into().expect("interner handles only u32"),
- after: tokens.after.len().try_into().expect("interner handles only u32"),
- }
- .into(),
- )));
- }
- }
- }
- Ok(None)
-}
-
-mod diff {
- use std::ops::Range;
-
- pub struct Statistics<'a, 'data> {
- pub removed_bytes: usize,
- pub input: &'a gix_diff::blob::intern::InternedInput<&'data [u8]>,
- }
-
- impl<'a, 'data> gix_diff::blob::Sink for Statistics<'a, 'data> {
- type Out = usize;
-
- fn process_change(&mut self, before: Range<u32>, _after: Range<u32>) {
- self.removed_bytes = self.input.before[before.start as usize..before.end as usize]
- .iter()
- .map(|token| self.input.interner[*token].len())
- .sum();
- }
-
- fn finish(self) -> Self::Out {
- self.removed_bytes
- }
- }
-}
-
-mod tree_to_events {
- use gix_diff::tree::visit::Change;
- use gix_object::tree::EntryRef;
-
- use crate::bstr::BStr;
-
- pub struct Delegate<'a> {
- parent: &'a mut super::State,
- recorder: gix_traverse::tree::Recorder,
- }
-
- impl<'a> Delegate<'a> {
- pub fn new(parent: &'a mut super::State) -> Self {
- let tracking = parent.tracking.map(|t| match t {
- gix_diff::tree::recorder::Location::FileName => gix_traverse::tree::recorder::Location::FileName,
- gix_diff::tree::recorder::Location::Path => gix_traverse::tree::recorder::Location::Path,
- });
- Self {
- parent,
- recorder: gix_traverse::tree::Recorder::default().track_location(tracking),
- }
- }
- }
-
- impl gix_traverse::tree::Visit for Delegate<'_> {
- fn pop_front_tracked_path_and_set_current(&mut self) {
- self.recorder.pop_front_tracked_path_and_set_current()
- }
-
- fn push_back_tracked_path_component(&mut self, component: &BStr) {
- self.recorder.push_back_tracked_path_component(component)
- }
-
- fn push_path_component(&mut self, component: &BStr) {
- self.recorder.push_path_component(component)
- }
-
- fn pop_path_component(&mut self) {
- self.recorder.pop_path_component();
- }
-
- fn visit_tree(&mut self, _entry: &EntryRef<'_>) -> gix_traverse::tree::visit::Action {
- gix_traverse::tree::visit::Action::Continue
- }
-
- fn visit_nontree(&mut self, entry: &EntryRef<'_>) -> gix_traverse::tree::visit::Action {
- if entry.mode.is_blob() {
- self.parent.try_push_change(
- Change::Modification {
- previous_entry_mode: entry.mode,
- previous_oid: gix_hash::ObjectId::null(entry.oid.kind()),
- entry_mode: entry.mode,
- oid: entry.oid.to_owned(),
- },
- self.recorder.path(),
- );
- // make sure these aren't viable to be emitted anymore.
- self.parent.items.last_mut().expect("just pushed").emitted = true;
- }
- gix_traverse::tree::visit::Action::Continue
- }
- }
-}
diff --git a/vendor/gix/src/object/tree/iter.rs b/vendor/gix/src/object/tree/iter.rs
index 848d9eeab..cc9c4bd28 100644
--- a/vendor/gix/src/object/tree/iter.rs
+++ b/vendor/gix/src/object/tree/iter.rs
@@ -51,7 +51,7 @@ impl<'repo, 'a> std::fmt::Display for EntryRef<'repo, 'a> {
write!(
f,
"{:06o} {:>6} {}\t{}",
- self.mode() as u32,
+ *self.mode(),
self.mode().as_str(),
self.id().shorten_or_id(),
self.filename()
diff --git a/vendor/gix/src/object/tree/mod.rs b/vendor/gix/src/object/tree/mod.rs
index e4dac24f8..a8173fce4 100644
--- a/vendor/gix/src/object/tree/mod.rs
+++ b/vendor/gix/src/object/tree/mod.rs
@@ -1,8 +1,7 @@
use gix_hash::ObjectId;
use gix_macros::momo;
-pub use gix_object::tree::EntryMode;
-use gix_object::{bstr::BStr, TreeRefIter};
-use gix_odb::FindExt;
+pub use gix_object::tree::{EntryKind, EntryMode};
+use gix_object::{bstr::BStr, FindExt, TreeRefIter};
use crate::{object::find, Id, ObjectDetached, Tree};
diff --git a/vendor/gix/src/object/tree/traverse.rs b/vendor/gix/src/object/tree/traverse.rs
index 974df6b0d..f2f3ff817 100644
--- a/vendor/gix/src/object/tree/traverse.rs
+++ b/vendor/gix/src/object/tree/traverse.rs
@@ -1,5 +1,3 @@
-use gix_odb::FindExt;
-
use crate::Tree;
/// Traversal
@@ -52,11 +50,6 @@ impl<'a, 'repo> Platform<'a, 'repo> {
{
let root = gix_object::TreeRefIter::from_bytes(&self.root.data);
let state = gix_traverse::tree::breadthfirst::State::default();
- gix_traverse::tree::breadthfirst(
- root,
- state,
- |oid, buf| self.root.repo.objects.find_tree_iter(oid, buf).ok(),
- delegate,
- )
+ gix_traverse::tree::breadthfirst(root, state, &self.root.repo.objects, delegate)
}
}
diff --git a/vendor/gix/src/open/repository.rs b/vendor/gix/src/open/repository.rs
index fde647a4e..6c3f07f42 100644
--- a/vendor/gix/src/open/repository.rs
+++ b/vendor/gix/src/open/repository.rs
@@ -8,7 +8,7 @@ use super::{Error, Options};
use crate::{
config,
config::{
- cache::{interpolate_context, util::ApplyLeniency},
+ cache::interpolate_context,
tree::{gitoxide, Core, Key, Safe},
},
open::Permissions,
@@ -237,13 +237,27 @@ impl ThreadSafeRepository {
.resolved
.path_filter("core", None, Core::WORKTREE.name, &mut filter_config_section)
{
+ let wt_clone = wt.clone();
let wt_path = wt
.interpolate(interpolate_context(git_install_dir.as_deref(), home.as_deref()))
- .map_err(config::Error::PathInterpolation)?;
- worktree_dir = {
- gix_path::normalize(git_dir.join(wt_path).into(), current_dir)
- .and_then(|wt| wt.as_ref().is_dir().then(|| wt.into_owned()))
+ .map_err(|err| config::Error::PathInterpolation {
+ path: wt_clone.value.into_owned(),
+ source: err,
+ })?;
+ worktree_dir = gix_path::normalize(git_dir.join(wt_path).into(), current_dir).map(Cow::into_owned);
+ #[allow(unused_variables)]
+ if let Some(worktree_path) = worktree_dir.as_deref().filter(|wtd| !wtd.is_dir()) {
+ gix_trace::warn!("The configured worktree path '{}' is not a directory or doesn't exist - `core.worktree` may be misleading", worktree_path.display());
}
+ } else if !config.lenient_config
+ && config
+ .resolved
+ .boolean_filter("core", None, Core::WORKTREE.name, &mut filter_config_section)
+ .is_some()
+ {
+ return Err(Error::from(config::Error::ConfigTypedString(
+ config::key::GenericErrorWithValue::from(&Core::WORKTREE),
+ )));
}
}
@@ -259,6 +273,7 @@ impl ThreadSafeRepository {
}
refs.write_reflog = config::cache::util::reflog_or_default(config.reflog, worktree_dir.is_some());
+ refs.namespace = config.refs_namespace.clone();
let replacements = replacement_objects_refs_prefix(&config.resolved, lenient_config, filter_config_section)?
.and_then(|prefix| {
let _span = gix_trace::detail!("find replacement objects");
@@ -310,11 +325,7 @@ fn replacement_objects_refs_prefix(
lenient: bool,
mut filter_config_section: fn(&gix_config::file::Metadata) -> bool,
) -> Result<Option<PathBuf>, Error> {
- let is_disabled = config
- .boolean_filter_by_key("core.useReplaceRefs", &mut filter_config_section)
- .map(|b| Core::USE_REPLACE_REFS.enrich_error(b))
- .transpose()
- .with_leniency(lenient)
+ let is_disabled = config::shared::is_replace_refs_enabled(config, lenient, filter_config_section)
.map_err(config::Error::ConfigBoolean)?
.unwrap_or(true);
diff --git a/vendor/gix/src/pathspec.rs b/vendor/gix/src/pathspec.rs
index 235a91d76..a56ad1c32 100644
--- a/vendor/gix/src/pathspec.rs
+++ b/vendor/gix/src/pathspec.rs
@@ -1,6 +1,5 @@
//! Pathspec plumbing and abstractions
use gix_macros::momo;
-use gix_odb::FindExt;
pub use gix_pathspec::*;
use crate::{bstr::BStr, AttributeStack, Pathspec, PathspecDetached, Repository};
@@ -122,9 +121,7 @@ impl<'repo> Pathspec<'repo> {
let stack = self.stack.as_mut().expect("initialized in advance");
stack
.set_case(case)
- .at_entry(relative_path, Some(is_dir), |id, buf| {
- self.repo.objects.find_blob(id, buf)
- })
+ .at_entry(relative_path, Some(is_dir), &self.repo.objects)
.map_or(false, |platform| platform.matching_attributes(out))
},
)
@@ -180,7 +177,7 @@ impl PathspecDetached {
let stack = self.stack.as_mut().expect("initialized in advance");
stack
.set_case(case)
- .at_entry(relative_path, Some(is_dir), |id, buf| self.odb.find_blob(id, buf))
+ .at_entry(relative_path, Some(is_dir), &self.odb)
.map_or(false, |platform| platform.matching_attributes(out))
},
)
diff --git a/vendor/gix/src/prelude.rs b/vendor/gix/src/prelude.rs
index 36fbfc7b1..8a3e01124 100644
--- a/vendor/gix/src/prelude.rs
+++ b/vendor/gix/src/prelude.rs
@@ -1,4 +1,5 @@
pub use gix_features::parallel::reduce::Finalize;
-pub use gix_odb::{Find, FindExt, Header, HeaderExt, Write};
+pub use gix_object::{Find, FindExt};
+pub use gix_odb::{Header, HeaderExt, Write};
pub use crate::ext::*;
diff --git a/vendor/gix/src/reference/edits.rs b/vendor/gix/src/reference/edits.rs
index 208340770..cba652630 100644
--- a/vendor/gix/src/reference/edits.rs
+++ b/vendor/gix/src/reference/edits.rs
@@ -1,9 +1,10 @@
///
pub mod set_target_id {
- use crate::{bstr::BString, Reference};
use gix_macros::momo;
use gix_ref::{transaction::PreviousValue, Target};
+ use crate::{bstr::BString, Reference};
+
mod error {
use gix_ref::FullName;
diff --git a/vendor/gix/src/reference/errors.rs b/vendor/gix/src/reference/errors.rs
index ff3802e94..d5b09f78e 100644
--- a/vendor/gix/src/reference/errors.rs
+++ b/vendor/gix/src/reference/errors.rs
@@ -43,9 +43,7 @@ pub mod head_id {
#[error(transparent)]
Head(#[from] crate::reference::find::existing::Error),
#[error(transparent)]
- PeelToId(#[from] crate::head::peel::Error),
- #[error("Branch '{name}' does not have any commits")]
- Unborn { name: gix_ref::FullName },
+ PeelToId(#[from] crate::head::peel::into_id::Error),
}
}
@@ -69,9 +67,7 @@ pub mod head_tree_id {
#[allow(missing_docs)]
pub enum Error {
#[error(transparent)]
- Head(#[from] crate::reference::find::existing::Error),
- #[error(transparent)]
- PeelToCommit(#[from] crate::head::peel::to_commit::Error),
+ HeadCommit(#[from] crate::reference::head_commit::Error),
#[error(transparent)]
DecodeCommit(#[from] gix_object::decode::Error),
}
diff --git a/vendor/gix/src/reference/iter.rs b/vendor/gix/src/reference/iter.rs
index a79a74743..604a8ac4b 100644
--- a/vendor/gix/src/reference/iter.rs
+++ b/vendor/gix/src/reference/iter.rs
@@ -2,7 +2,6 @@
use std::path::Path;
use gix_macros::momo;
-use gix_odb::pack::Find;
use gix_ref::file::ReferenceExt;
/// A platform to create iterators over references.
@@ -96,15 +95,10 @@ impl<'r> Iterator for Iter<'r> {
res.map_err(|err| Box::new(err) as Box<dyn std::error::Error + Send + Sync + 'static>)
.and_then(|mut r| {
if self.peel {
- let handle = &self.repo;
- r.peel_to_id_in_place(&handle.refs, &mut |oid, buf| {
- handle
- .objects
- .try_find(oid.as_ref(), buf)
- .map(|po| po.map(|(o, _l)| (o.kind, o.data)))
- })
- .map_err(|err| Box::new(err) as Box<dyn std::error::Error + Send + Sync + 'static>)
- .map(|_| r)
+ let repo = &self.repo;
+ r.peel_to_id_in_place(&repo.refs, &repo.objects)
+ .map_err(|err| Box::new(err) as Box<dyn std::error::Error + Send + Sync + 'static>)
+ .map(|_| r)
} else {
Ok(r)
}
diff --git a/vendor/gix/src/reference/mod.rs b/vendor/gix/src/reference/mod.rs
index 2d32f595d..ebdbf66a7 100644
--- a/vendor/gix/src/reference/mod.rs
+++ b/vendor/gix/src/reference/mod.rs
@@ -1,6 +1,5 @@
//!
-use gix_odb::pack::Find;
use gix_ref::file::ReferenceExt;
use crate::{Id, Reference};
@@ -69,13 +68,8 @@ impl<'repo> Reference<'repo> {
///
/// This is useful to learn where this reference is ultimately pointing to.
pub fn peel_to_id_in_place(&mut self) -> Result<Id<'repo>, peel::Error> {
- let repo = &self.repo;
- let oid = self.inner.peel_to_id_in_place(&repo.refs, &mut |oid, buf| {
- repo.objects
- .try_find(&oid, buf)
- .map(|po| po.map(|(o, _l)| (o.kind, o.data)))
- })?;
- Ok(Id::from_id(oid, repo))
+ let oid = self.inner.peel_to_id_in_place(&self.repo.refs, &self.repo.objects)?;
+ Ok(Id::from_id(oid, self.repo))
}
/// Similar to [`peel_to_id_in_place()`][Reference::peel_to_id_in_place()], but consumes this instance.
diff --git a/vendor/gix/src/remote/connect.rs b/vendor/gix/src/remote/connect.rs
index 6acc9f67f..df2b36230 100644
--- a/vendor/gix/src/remote/connect.rs
+++ b/vendor/gix/src/remote/connect.rs
@@ -1,8 +1,9 @@
#![allow(clippy::result_large_err)]
-use gix_protocol::transport::client::Transport;
use std::borrow::Cow;
+use gix_protocol::transport::client::Transport;
+
use crate::{remote::Connection, Remote};
mod error {
@@ -57,11 +58,13 @@ impl<'repo> Remote<'repo> {
where
T: Transport,
{
+ let trace = self.repo.config.trace_packet();
Connection {
remote: self,
authenticate: None,
transport_options: None,
transport,
+ trace,
}
}
@@ -91,6 +94,7 @@ impl<'repo> Remote<'repo> {
.then(|| self.repo.ssh_connect_options())
.transpose()?
.unwrap_or_default(),
+ trace: self.repo.config.trace_packet(),
},
)
.await?;
diff --git a/vendor/gix/src/remote/connection/fetch/mod.rs b/vendor/gix/src/remote/connection/fetch/mod.rs
index 8327d5abc..d4afd1023 100644
--- a/vendor/gix/src/remote/connection/fetch/mod.rs
+++ b/vendor/gix/src/remote/connection/fetch/mod.rs
@@ -281,12 +281,13 @@ where
// connection in an async context.
gix_protocol::futures_lite::future::block_on(gix_protocol::indicate_end_of_interaction(
&mut con.transport,
+ con.trace,
))
.ok();
}
#[cfg(not(feature = "async-network-client"))]
{
- gix_protocol::indicate_end_of_interaction(&mut con.transport).ok();
+ gix_protocol::indicate_end_of_interaction(&mut con.transport, con.trace).ok();
}
}
}
diff --git a/vendor/gix/src/remote/connection/fetch/negotiate.rs b/vendor/gix/src/remote/connection/fetch/negotiate.rs
index 92a141f6f..f5b6a031c 100644
--- a/vendor/gix/src/remote/connection/fetch/negotiate.rs
+++ b/vendor/gix/src/remote/connection/fetch/negotiate.rs
@@ -16,7 +16,7 @@ pub enum Error {
#[error("We were unable to figure out what objects the server should send after {rounds} round(s)")]
NegotiationFailed { rounds: usize },
#[error(transparent)]
- LookupCommitInGraph(#[from] gix_revwalk::graph::lookup::commit::Error),
+ LookupCommitInGraph(#[from] gix_revwalk::graph::try_lookup_or_insert_default::Error),
#[error(transparent)]
InitRefsIterator(#[from] crate::reference::iter::init::Error),
#[error(transparent)]
diff --git a/vendor/gix/src/remote/connection/fetch/receive_pack.rs b/vendor/gix/src/remote/connection/fetch/receive_pack.rs
index 18e5ac159..7634b34cf 100644
--- a/vendor/gix/src/remote/connection/fetch/receive_pack.rs
+++ b/vendor/gix/src/remote/connection/fetch/receive_pack.rs
@@ -3,7 +3,7 @@ use std::{
sync::atomic::{AtomicBool, Ordering},
};
-use gix_odb::{store::RefreshMode, FindExt};
+use gix_odb::store::RefreshMode;
use gix_protocol::{
fetch::Arguments,
transport::{client::Transport, packetline::read::ProgressAction},
@@ -104,7 +104,7 @@ where
gix_protocol::fetch::Response::check_required_features(protocol_version, &fetch_features)?;
let sideband_all = fetch_features.iter().any(|(n, _)| *n == "sideband-all");
- let mut arguments = gix_protocol::fetch::Arguments::new(protocol_version, fetch_features);
+ let mut arguments = gix_protocol::fetch::Arguments::new(protocol_version, fetch_features, con.trace);
if matches!(con.remote.fetch_tags, crate::remote::fetch::Tags::Included) {
if !arguments.can_use_include_tag() {
return Err(Error::MissingServerFeature {
@@ -125,7 +125,10 @@ where
});
}
- let negotiate_span = gix_trace::detail!("negotiate");
+ let negotiate_span = gix_trace::detail!(
+ "negotiate",
+ protocol_version = self.ref_map.handshake.server_protocol_version as usize
+ );
let mut negotiator = repo
.config
.resolved
@@ -155,7 +158,9 @@ where
let mut previous_response = None::<gix_protocol::fetch::Response>;
let (mut write_pack_bundle, negotiate) = match &action {
negotiate::Action::NoChange | negotiate::Action::SkipToRefUpdate => {
- gix_protocol::indicate_end_of_interaction(&mut con.transport).await.ok();
+ gix_protocol::indicate_end_of_interaction(&mut con.transport, con.trace)
+ .await
+ .ok();
(None, None)
}
negotiate::Action::MustNegotiate {
@@ -206,7 +211,9 @@ where
is_done
}
Err(err) => {
- gix_protocol::indicate_end_of_interaction(&mut con.transport).await.ok();
+ gix_protocol::indicate_end_of_interaction(&mut con.transport, con.trace)
+ .await
+ .ok();
return Err(err.into());
}
};
@@ -214,8 +221,13 @@ where
if sideband_all {
setup_remote_progress(progress, &mut reader, should_interrupt);
}
- let response =
- gix_protocol::fetch::Response::from_line_reader(protocol_version, &mut reader, is_done).await?;
+ let response = gix_protocol::fetch::Response::from_line_reader(
+ protocol_version,
+ &mut reader,
+ is_done,
+ !is_done,
+ )
+ .await?;
let has_pack = response.has_pack();
previous_response = Some(response);
if has_pack {
@@ -265,14 +277,23 @@ where
should_interrupt,
Some(Box::new({
let repo = repo.clone();
- move |oid, buf| repo.objects.find(&oid, buf).ok()
+ repo.objects
})),
options,
)?;
+ // Assure the final flush packet is consumed.
+ #[cfg(feature = "async-network-client")]
+ let has_read_to_end = { rd.get_ref().stopped_at().is_some() };
+ #[cfg(not(feature = "async-network-client"))]
+ let has_read_to_end = { rd.stopped_at().is_some() };
+ if !has_read_to_end {
+ std::io::copy(&mut rd, &mut std::io::sink()).unwrap();
+ }
#[cfg(feature = "async-network-client")]
{
reader = rd.into_inner();
}
+
#[cfg(not(feature = "async-network-client"))]
{
reader = rd;
@@ -284,7 +305,9 @@ where
drop(reader);
if matches!(protocol_version, gix_protocol::transport::Protocol::V2) {
- gix_protocol::indicate_end_of_interaction(&mut con.transport).await.ok();
+ gix_protocol::indicate_end_of_interaction(&mut con.transport, con.trace)
+ .await
+ .ok();
}
if let Some(shallow_lock) = shallow_lock {
@@ -387,24 +410,14 @@ fn add_shallow_args(
Ok((shallow_commits, shallow_lock))
}
-fn setup_remote_progress(
+fn setup_remote_progress<'a>(
progress: &mut dyn crate::DynNestedProgress,
- reader: &mut Box<dyn gix_protocol::transport::client::ExtendedBufRead + Unpin + '_>,
- should_interrupt: &AtomicBool,
+ reader: &mut Box<dyn gix_protocol::transport::client::ExtendedBufRead<'a> + Unpin + 'a>,
+ should_interrupt: &'a AtomicBool,
) {
use gix_protocol::transport::client::ExtendedBufRead;
reader.set_progress_handler(Some(Box::new({
let mut remote_progress = progress.add_child_with_id("remote".to_string(), 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);
if should_interrupt.load(Ordering::Relaxed) {
@@ -413,5 +426,5 @@ fn setup_remote_progress(
ProgressAction::Continue
}
}
- }) as gix_protocol::transport::client::HandleProgress));
+ }) as gix_protocol::transport::client::HandleProgress<'a>));
}
diff --git a/vendor/gix/src/remote/connection/fetch/update_refs/mod.rs b/vendor/gix/src/remote/connection/fetch/update_refs/mod.rs
index 3d6fb18bd..c487e7f5c 100644
--- a/vendor/gix/src/remote/connection/fetch/update_refs/mod.rs
+++ b/vendor/gix/src/remote/connection/fetch/update_refs/mod.rs
@@ -1,7 +1,7 @@
#![allow(clippy::result_large_err)]
use std::{collections::BTreeMap, convert::TryInto, path::PathBuf};
-use gix_odb::{Find, FindExt};
+use gix_object::Exists;
use gix_ref::{
transaction::{Change, LogChange, PreviousValue, RefEdit, RefLog},
Target, TargetRef,
@@ -96,8 +96,8 @@ pub(crate) fn update(
) {
// `None` only if unborn.
let remote_id = remote.as_id();
- if matches!(dry_run, fetch::DryRun::No) && !remote_id.map_or(true, |id| repo.objects.contains(id)) {
- if let Some(remote_id) = remote_id.filter(|id| !repo.objects.contains(id)) {
+ if matches!(dry_run, fetch::DryRun::No) && !remote_id.map_or(true, |id| repo.objects.exists(id)) {
+ if let Some(remote_id) = remote_id.filter(|id| !repo.objects.exists(id)) {
let update = if is_implicit_tag {
Mode::ImplicitTagNotSentByRemote.into()
} else {
@@ -159,7 +159,7 @@ pub(crate) fn update(
}).and_then(|local_commit_time|
remote_id
.to_owned()
- .ancestors(|id, buf| repo.objects.find_commit_iter(id, buf))
+ .ancestors(&repo.objects)
.sorting(
gix_traverse::commit::Sorting::ByCommitTimeNewestFirstCutoffOlderThan {
seconds: local_commit_time
@@ -325,11 +325,7 @@ pub(crate) fn update(
.packed_refs(
match write_packed_refs {
fetch::WritePackedRefs::Only => {
- gix_ref::file::transaction::PackedRefs::DeletionsAndNonSymbolicUpdatesRemoveLooseSourceReference(Box::new(|oid, buf| {
- repo.objects
- .try_find(&oid, buf)
- .map(|obj| obj.map(|obj| obj.kind))
- }))},
+ gix_ref::file::transaction::PackedRefs::DeletionsAndNonSymbolicUpdatesRemoveLooseSourceReference(Box::new(&repo.objects))},
fetch::WritePackedRefs::Never => gix_ref::file::transaction::PackedRefs::DeletionsOnly
}
)
diff --git a/vendor/gix/src/remote/connection/fetch/update_refs/tests.rs b/vendor/gix/src/remote/connection/fetch/update_refs/tests.rs
index 0b29f14f4..27501720d 100644
--- a/vendor/gix/src/remote/connection/fetch/update_refs/tests.rs
+++ b/vendor/gix/src/remote/connection/fetch/update_refs/tests.rs
@@ -191,7 +191,7 @@ mod update {
#[test]
fn checked_out_branches_in_worktrees_are_rejected_with_additional_information() -> Result {
- let root = gix_path::realpath(&gix_testtools::scripted_fixture_read_only_with_args(
+ let root = gix_path::realpath(gix_testtools::scripted_fixture_read_only_with_args(
"make_fetch_repos.sh",
[base_repo_path()],
)?)?;
diff --git a/vendor/gix/src/remote/connection/mod.rs b/vendor/gix/src/remote/connection/mod.rs
index 02a09926a..f9b8aa7e6 100644
--- a/vendor/gix/src/remote/connection/mod.rs
+++ b/vendor/gix/src/remote/connection/mod.rs
@@ -17,6 +17,7 @@ pub struct Connection<'a, 'repo, T> {
pub(crate) authenticate: Option<AuthenticateFn<'a>>,
pub(crate) transport_options: Option<Box<dyn std::any::Any>>,
pub(crate) transport: T,
+ pub(crate) trace: bool,
}
mod access;
diff --git a/vendor/gix/src/remote/connection/ref_map.rs b/vendor/gix/src/remote/connection/ref_map.rs
index f1b40d56e..bf53cf35f 100644
--- a/vendor/gix/src/remote/connection/ref_map.rs
+++ b/vendor/gix/src/remote/connection/ref_map.rs
@@ -95,7 +95,7 @@ where
#[gix_protocol::maybe_async::maybe_async]
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)
+ gix_protocol::indicate_end_of_interaction(&mut self.transport, self.trace)
.await
.ok();
res
@@ -112,6 +112,7 @@ where
mut extra_refspecs,
}: Options,
) -> Result<fetch::RefMap, Error> {
+ let _span = gix_trace::coarse!("remote::Connection::ref_map()");
let null = gix_hash::ObjectId::null(gix_hash::Kind::Sha1); // OK to hardcode Sha1, it's not supposed to match, ever.
if let Some(tag_spec) = self.remote.fetch_tags.to_refspec().map(|spec| spec.to_owned()) {
@@ -186,6 +187,7 @@ where
refspecs: &[gix_refspec::RefSpec],
mut progress: impl Progress,
) -> Result<HandshakeWithRefs, Error> {
+ let _span = gix_trace::coarse!("remote::Connection::fetch_refs()");
let mut credentials_storage;
let url = self.transport.to_url();
let authenticate = match self.authenticate.as_mut() {
@@ -241,6 +243,7 @@ where
Ok(gix_protocol::ls_refs::Action::Continue)
},
&mut progress,
+ self.trace,
)
.await?
}
diff --git a/vendor/gix/src/repository/config/mod.rs b/vendor/gix/src/repository/config/mod.rs
index 618ccf0f6..6966e1276 100644
--- a/vendor/gix/src/repository/config/mod.rs
+++ b/vendor/gix/src/repository/config/mod.rs
@@ -79,6 +79,52 @@ impl crate::Repository {
Ok(opts)
}
+ /// Return the context to be passed to any spawned program that is supposed to interact with the repository, like
+ /// hooks or filters.
+ #[cfg(feature = "attributes")]
+ pub fn command_context(&self) -> Result<gix_command::Context, config::command_context::Error> {
+ use crate::config::{
+ cache::util::ApplyLeniency,
+ tree::{gitoxide, Key},
+ };
+
+ let pathspec_boolean = |key: &'static config::tree::keys::Boolean| {
+ self.config
+ .resolved
+ .boolean("gitoxide", Some("pathspec".into()), key.name())
+ .map(|value| key.enrich_error(value))
+ .transpose()
+ .with_leniency(self.config.lenient_config)
+ };
+
+ Ok(gix_command::Context {
+ stderr: {
+ let key = &gitoxide::Core::EXTERNAL_COMMAND_STDERR;
+ self.config
+ .resolved
+ .boolean("gitoxide", Some("core".into()), key.name())
+ .map(|value| key.enrich_error(value))
+ .transpose()
+ .with_leniency(self.config.lenient_config)?
+ .unwrap_or(true)
+ .into()
+ },
+ git_dir: self.git_dir().to_owned().into(),
+ worktree_dir: self.work_dir().map(ToOwned::to_owned),
+ no_replace_objects: config::shared::is_replace_refs_enabled(
+ &self.config.resolved,
+ self.config.lenient_config,
+ self.filter_config_section(),
+ )?
+ .map(|enabled| !enabled),
+ ref_namespace: self.refs.namespace.as_ref().map(|ns| ns.as_bstr().to_owned()),
+ literal_pathspecs: pathspec_boolean(&gitoxide::Pathspec::LITERAL)?,
+ glob_pathspecs: pathspec_boolean(&gitoxide::Pathspec::GLOB)?
+ .or(pathspec_boolean(&gitoxide::Pathspec::NOGLOB)?),
+ icase_pathspecs: pathspec_boolean(&gitoxide::Pathspec::ICASE)?,
+ })
+ }
+
/// The kind of object hash the repository is configured to use.
pub fn object_hash(&self) -> gix_hash::Kind {
self.config.object_hash
diff --git a/vendor/gix/src/repository/config/transport.rs b/vendor/gix/src/repository/config/transport.rs
index 99b5a7f47..907e2a4fb 100644
--- a/vendor/gix/src/repository/config/transport.rs
+++ b/vendor/gix/src/repository/config/transport.rs
@@ -405,6 +405,30 @@ impl crate::Repository {
}
}
+ {
+ let key = "gitoxide.http.sslNoVerify";
+ let ssl_no_verify = config
+ .boolean_filter_by_key(key, &mut trusted_only)
+ .map(|value| config::tree::gitoxide::Http::SSL_NO_VERIFY.enrich_error(value))
+ .transpose()
+ .with_leniency(lenient)
+ .map_err(config::transport::http::Error::from)?
+ .unwrap_or_default();
+
+ if ssl_no_verify {
+ opts.ssl_verify = false;
+ } else {
+ let key = "http.sslVerify";
+ opts.ssl_verify = config
+ .boolean_filter_by_key(key, &mut trusted_only)
+ .map(|value| config::tree::Http::SSL_VERIFY.enrich_error(value))
+ .transpose()
+ .with_leniency(lenient)
+ .map_err(config::transport::http::Error::from)?
+ .unwrap_or(true);
+ }
+ }
+
#[cfg(feature = "blocking-http-transport-curl")]
{
let key = "http.schannelCheckRevoke";
diff --git a/vendor/gix/src/repository/diff.rs b/vendor/gix/src/repository/diff.rs
new file mode 100644
index 000000000..cb1d070a2
--- /dev/null
+++ b/vendor/gix/src/repository/diff.rs
@@ -0,0 +1,45 @@
+use crate::Repository;
+
+///
+pub mod resource_cache {
+ /// The error returned by [Repository::diff_resource_cache()](super::Repository::diff_resource_cache()).
+ #[derive(Debug, thiserror::Error)]
+ #[allow(missing_docs)]
+ pub enum Error {
+ #[error("Could not obtain resource cache for diffing")]
+ ResourceCache(#[from] crate::diff::resource_cache::Error),
+ #[error(transparent)]
+ Index(#[from] crate::repository::index_or_load_from_head::Error),
+ }
+}
+
+/// Diff-utilities
+impl Repository {
+ /// Create a resource cache for diffable objects, and configured with everything it needs to know to perform diffs
+ /// faithfully just like `git` would.
+ /// `mode` controls what version of a resource should be diffed.
+ /// `worktree_roots` determine if files can be read from the worktree, where each side of the diff operation can
+ /// be represented by its own worktree root. `.gitattributes` are automatically read from the worktree if at least
+ /// one worktree is present.
+ ///
+ /// Note that attributes will always be obtained from the current `HEAD` index even if the resources being diffed
+ /// might live in another tree. Further, if one of the `worktree_roots` are set, attributes will also be read from
+ /// the worktree. Otherwise, it will be skipped and attributes are read from the index tree instead.
+ pub fn diff_resource_cache(
+ &self,
+ mode: gix_diff::blob::pipeline::Mode,
+ worktree_roots: gix_diff::blob::pipeline::WorktreeRoots,
+ ) -> Result<gix_diff::blob::Platform, resource_cache::Error> {
+ Ok(crate::diff::resource_cache(
+ self,
+ &*self.index_or_load_from_head()?,
+ mode,
+ if worktree_roots.new_root.is_some() || worktree_roots.old_root.is_some() {
+ gix_worktree::stack::state::attributes::Source::WorktreeThenIdMapping
+ } else {
+ gix_worktree::stack::state::attributes::Source::IdMapping
+ },
+ worktree_roots,
+ )?)
+ }
+}
diff --git a/vendor/gix/src/repository/filter.rs b/vendor/gix/src/repository/filter.rs
index 3aacb1a3d..68644ca98 100644
--- a/vendor/gix/src/repository/filter.rs
+++ b/vendor/gix/src/repository/filter.rs
@@ -2,7 +2,7 @@ use crate::{filter, repository::IndexPersistedOrInMemory, Id, Repository};
///
pub mod pipeline {
- /// The error returned by [Repository::filter_pipeline()][super::Repository::filter_pipeline()].
+ /// The error returned by [Repository::filter_pipeline()](super::Repository::filter_pipeline()).
#[derive(Debug, thiserror::Error)]
#[allow(missing_docs)]
pub enum Error {
@@ -24,7 +24,7 @@ pub mod pipeline {
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()].
+ /// [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.
diff --git a/vendor/gix/src/repository/graph.rs b/vendor/gix/src/repository/graph.rs
index f4f2b18cc..7d59589ed 100644
--- a/vendor/gix/src/repository/graph.rs
+++ b/vendor/gix/src/repository/graph.rs
@@ -1,5 +1,3 @@
-use gix_odb::Find;
-
impl crate::Repository {
/// Create a graph data-structure capable of accelerating graph traversals and storing state of type `T` with each commit
/// it encountered.
@@ -16,11 +14,7 @@ impl crate::Repository {
/// of the commit walk.
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(gix_object::Data::try_into_commit_iter))
- },
+ &self.objects,
self.config
.may_use_commit_graph()
.unwrap_or(true)
diff --git a/vendor/gix/src/repository/index.rs b/vendor/gix/src/repository/index.rs
index 59666fc5f..85a1a664b 100644
--- a/vendor/gix/src/repository/index.rs
+++ b/vendor/gix/src/repository/index.rs
@@ -1,5 +1,3 @@
-use gix_odb::FindExt;
-
use crate::{config::cache::util::ApplyLeniencyDefault, repository::IndexPersistedOrInMemory, worktree};
/// Index access
@@ -113,7 +111,7 @@ impl crate::Repository {
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())?,
+ gix_index::State::from_tree(tree, &self.objects)?,
self.git_dir().join("index"),
))
}
diff --git a/vendor/gix/src/repository/kind.rs b/vendor/gix/src/repository/kind.rs
index 88779e0cc..a9af07e23 100644
--- a/vendor/gix/src/repository/kind.rs
+++ b/vendor/gix/src/repository/kind.rs
@@ -13,7 +13,7 @@ impl From<gix_discover::repository::Kind> for Kind {
gix_discover::repository::Kind::Submodule { .. } | gix_discover::repository::Kind::SubmoduleGitDir => {
Kind::WorkTree { is_linked: false }
}
- gix_discover::repository::Kind::Bare => Kind::Bare,
+ gix_discover::repository::Kind::PossiblyBare => Kind::Bare,
gix_discover::repository::Kind::WorkTreeGitDir { .. } => Kind::WorkTree { is_linked: true },
gix_discover::repository::Kind::WorkTree { linked_git_dir } => Kind::WorkTree {
is_linked: linked_git_dir.is_some(),
diff --git a/vendor/gix/src/repository/mod.rs b/vendor/gix/src/repository/mod.rs
index e3742894b..28aa5aa8e 100644
--- a/vendor/gix/src/repository/mod.rs
+++ b/vendor/gix/src/repository/mod.rs
@@ -40,6 +40,9 @@ pub mod attributes;
mod cache;
mod config;
///
+#[cfg(feature = "blob-diff")]
+pub mod diff;
+///
#[cfg(feature = "attributes")]
pub mod filter;
mod graph;
@@ -124,6 +127,8 @@ pub mod worktree_stream {
AttributesCache(#[from] crate::config::attribute_stack::Error),
#[error(transparent)]
FilterPipeline(#[from] crate::filter::pipeline::options::Error),
+ #[error(transparent)]
+ CommandContext(#[from] crate::config::command_context::Error),
#[error("Needed {id} to be a tree to turn into a workspace stream, got {actual}")]
NotATree {
id: gix_hash::ObjectId,
diff --git a/vendor/gix/src/repository/object.rs b/vendor/gix/src/repository/object.rs
index 0b894939f..77f188bad 100644
--- a/vendor/gix/src/repository/object.rs
+++ b/vendor/gix/src/repository/object.rs
@@ -3,7 +3,8 @@ use std::{convert::TryInto, ops::DerefMut};
use gix_hash::ObjectId;
use gix_macros::momo;
-use gix_odb::{Find, FindExt, Header, HeaderExt, Write};
+use gix_object::{Exists, Find, FindExt};
+use gix_odb::{Header, HeaderExt, Write};
use gix_ref::{
transaction::{LogChange, PreviousValue, RefLog},
FullName,
@@ -70,7 +71,7 @@ impl crate::Repository {
if id == ObjectId::empty_tree(self.object_hash()) {
true
} else {
- self.objects.contains(id)
+ self.objects.exists(id)
}
}
@@ -140,7 +141,7 @@ impl crate::Repository {
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) {
+ if self.objects.exists(&oid) {
return Ok(oid.attach(self));
}
@@ -158,7 +159,7 @@ impl crate::Repository {
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.exists(&oid) {
return Ok(oid.attach(self));
}
self.objects
@@ -185,7 +186,7 @@ impl crate::Repository {
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) {
+ if self.objects.exists(&oid) {
return Ok(oid.attach(self));
}
diff --git a/vendor/gix/src/repository/reference.rs b/vendor/gix/src/repository/reference.rs
index e57ca63c0..b977c6ea8 100644
--- a/vendor/gix/src/repository/reference.rs
+++ b/vendor/gix/src/repository/reference.rs
@@ -174,7 +174,8 @@ impl crate::Repository {
.attach(self))
}
- /// Resolve the `HEAD` reference, follow and peel its target and obtain its object id.
+ /// Resolve the `HEAD` reference, follow and peel its target and obtain its object id,
+ /// following symbolic references and tags until a commit is found.
///
/// Note that this may fail for various reasons, most notably because the repository
/// is freshly initialized and doesn't have any commits yet.
@@ -182,12 +183,7 @@ impl crate::Repository {
/// Also note that the returned id is likely to point to a commit, but could also
/// point to a tree or blob. It won't, however, point to a tag as these are always peeled.
pub fn head_id(&self) -> Result<crate::Id<'_>, reference::head_id::Error> {
- let mut head = self.head()?;
- head.peel_to_id_in_place()
- .ok_or_else(|| reference::head_id::Error::Unborn {
- name: head.referent_name().expect("unborn").to_owned(),
- })?
- .map_err(Into::into)
+ Ok(self.head()?.into_peeled_id()?)
}
/// Return the name to the symbolic reference `HEAD` points to, or `None` if the head is detached.
@@ -203,7 +199,8 @@ impl crate::Repository {
Ok(self.head()?.try_into_referent())
}
- /// Return the commit object the `HEAD` reference currently points to after peeling it fully.
+ /// Return the commit object the `HEAD` reference currently points to after peeling it fully,
+ /// following symbolic references and tags until a commit is found.
///
/// Note that this may fail for various reasons, most notably because the repository
/// is freshly initialized and doesn't have any commits yet. It could also fail if the
@@ -212,13 +209,14 @@ impl crate::Repository {
Ok(self.head()?.peel_to_commit_in_place()?)
}
- /// Return the tree id the `HEAD` reference currently points to after peeling it fully.
+ /// Return the tree id the `HEAD` reference currently points to after peeling it fully,
+ /// following symbolic references and tags until a commit is found.
///
/// Note that this may fail for various reasons, most notably because the repository
/// is freshly initialized and doesn't have any commits yet. It could also fail if the
/// head does not point to a commit.
pub fn head_tree_id(&self) -> Result<crate::Id<'_>, reference::head_tree_id::Error> {
- Ok(self.head()?.peel_to_commit_in_place()?.tree_id()?)
+ Ok(self.head_commit()?.tree_id()?)
}
/// Find the reference with the given partial or full `name`, like `main`, `HEAD`, `heads/branch` or `origin/other`,
diff --git a/vendor/gix/src/repository/revision.rs b/vendor/gix/src/repository/revision.rs
index bb9b56d57..b59a3a94d 100644
--- a/vendor/gix/src/repository/revision.rs
+++ b/vendor/gix/src/repository/revision.rs
@@ -1,6 +1,7 @@
-use crate::{bstr::BStr, revision, Id};
use gix_macros::momo;
+use crate::{bstr::BStr, revision, Id};
+
/// Methods for resolving revisions by spec or working with the commit graph.
impl crate::Repository {
/// Parse a revision specification and turn it into the object(s) it describes, similar to `git rev-parse`.
diff --git a/vendor/gix/src/repository/worktree.rs b/vendor/gix/src/repository/worktree.rs
index cc6f0bf73..529243896 100644
--- a/vendor/gix/src/repository/worktree.rs
+++ b/vendor/gix/src/repository/worktree.rs
@@ -62,7 +62,7 @@ impl crate::Repository {
&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};
+ use gix_odb::HeaderExt;
let id = id.into();
let header = self.objects.header(id)?;
if !header.kind().is_tree() {
@@ -79,18 +79,14 @@ impl crate::Repository {
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 pipeline = gix_filter::Pipeline::new(self.command_context()?, 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)
- },
+ objects.clone(),
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))?;
+ let entry = cache.at_entry(path, Some(mode.is_tree()), &objects)?;
entry.matching_attributes(attrs);
Ok(())
},
diff --git a/vendor/gix/src/revision/mod.rs b/vendor/gix/src/revision/mod.rs
index 3de528ecd..62fe72dd3 100644
--- a/vendor/gix/src/revision/mod.rs
+++ b/vendor/gix/src/revision/mod.rs
@@ -7,6 +7,7 @@ pub use gix_revision as plumbing;
///
pub mod walk;
+use crate::bstr::BString;
pub use walk::iter::Walk;
///
@@ -22,6 +23,8 @@ pub mod spec;
#[cfg(feature = "revision")]
pub struct Spec<'repo> {
pub(crate) inner: gix_revision::Spec,
+ /// The path we encountered in the revspec, like `@:<path>` or `@..@~1:<path>`.
+ pub(crate) path: Option<(BString, gix_object::tree::EntryMode)>,
/// The first name of a reference as seen while parsing a `RevSpec`, for completeness.
pub(crate) first_ref: Option<gix_ref::Reference>,
/// The second name of a reference as seen while parsing a `RevSpec`, for completeness.
diff --git a/vendor/gix/src/revision/spec/mod.rs b/vendor/gix/src/revision/spec/mod.rs
index a6a6eb739..af58ecdff 100644
--- a/vendor/gix/src/revision/spec/mod.rs
+++ b/vendor/gix/src/revision/spec/mod.rs
@@ -1,3 +1,4 @@
+use crate::bstr::BStr;
use crate::{ext::ReferenceExt, revision::Spec, Id, Reference};
///
@@ -37,6 +38,7 @@ impl<'repo> Spec<'repo> {
pub fn from_id(id: Id<'repo>) -> Self {
Spec {
inner: gix_revision::Spec::Include(id.inner),
+ path: None,
repo: id.repo,
first_ref: None,
second_ref: None,
@@ -62,6 +64,13 @@ impl<'repo> Spec<'repo> {
)
}
+ /// Return the path encountered in specs like `@:<path>` or `:<path>`, along with the kind of object it represents.
+ ///
+ /// Note that there can only be one as paths always terminates further revspec parsing.
+ pub fn path_and_mode(&self) -> Option<(&BStr, gix_object::tree::EntryMode)> {
+ self.path.as_ref().map(|(p, mode)| (p.as_ref(), *mode))
+ }
+
/// Return the name of the first reference we encountered while resolving the rev-spec, or `None` if a short hash
/// was used. For example, `@` might yield `Some(HEAD)`, but `abcd` yields `None`.
pub fn first_reference(&self) -> Option<&gix_ref::Reference> {
diff --git a/vendor/gix/src/revision/spec/parse/delegate/mod.rs b/vendor/gix/src/revision/spec/parse/delegate/mod.rs
index eaf7f5fd6..374906eaf 100644
--- a/vendor/gix/src/revision/spec/parse/delegate/mod.rs
+++ b/vendor/gix/src/revision/spec/parse/delegate/mod.rs
@@ -17,6 +17,7 @@ impl<'repo> Delegate<'repo> {
Delegate {
refs: Default::default(),
objs: Default::default(),
+ paths: Default::default(),
ambiguous_objects: Default::default(),
idx: 0,
kind: None,
@@ -100,6 +101,7 @@ impl<'repo> Delegate<'repo> {
let range = zero_or_one_objects_or_ambiguity_err(self.objs, self.prefix, self.err, self.repo)?;
Ok(crate::revision::Spec {
+ path: self.paths[0].take().or(self.paths[1].take()),
first_ref: self.refs[0].take(),
second_ref: self.refs[1].take(),
inner: kind_to_spec(self.kind, range)?,
diff --git a/vendor/gix/src/revision/spec/parse/delegate/navigate.rs b/vendor/gix/src/revision/spec/parse/delegate/navigate.rs
index 51feb1d76..731a24136 100644
--- a/vendor/gix/src/revision/spec/parse/delegate/navigate.rs
+++ b/vendor/gix/src/revision/spec/parse/delegate/navigate.rs
@@ -121,7 +121,7 @@ impl<'repo> delegate::Navigate for Delegate<'repo> {
let lookup_path = |obj: &ObjectId| {
let tree_id = peel(repo, obj, gix_object::Kind::Tree)?;
if path.is_empty() {
- return Ok(tree_id);
+ return Ok((tree_id, gix_object::tree::EntryKind::Tree.into()));
}
let mut tree = repo.find_object(tree_id)?.into_tree();
let entry =
@@ -131,11 +131,17 @@ impl<'repo> delegate::Navigate for Delegate<'repo> {
object: obj.attach(repo).shorten_or_id(),
tree: tree_id.attach(repo).shorten_or_id(),
})?;
- Ok(entry.object_id())
+ Ok((entry.object_id(), entry.mode()))
};
for obj in objs.iter() {
match lookup_path(obj) {
- Ok(replace) => replacements.push((*obj, replace)),
+ Ok((replace, mode)) => {
+ if !path.is_empty() {
+ // Technically this is letting the last one win, but so be it.
+ self.paths[self.idx] = Some((path.to_owned(), mode));
+ }
+ replacements.push((*obj, replace))
+ }
Err(err) => errors.push((*obj, err)),
}
}
@@ -306,6 +312,14 @@ impl<'repo> delegate::Navigate for Delegate<'repo> {
self.objs[self.idx]
.get_or_insert_with(HashSet::default)
.insert(entry.id);
+
+ self.paths[self.idx] = Some((
+ path.to_owned(),
+ entry
+ .mode
+ .to_tree_entry_mode()
+ .unwrap_or(gix_object::tree::EntryKind::Blob.into()),
+ ));
Some(())
}
None => {
diff --git a/vendor/gix/src/revision/spec/parse/mod.rs b/vendor/gix/src/revision/spec/parse/mod.rs
index 950dfa004..e45847763 100644
--- a/vendor/gix/src/revision/spec/parse/mod.rs
+++ b/vendor/gix/src/revision/spec/parse/mod.rs
@@ -7,6 +7,7 @@ use gix_revision::spec::parse;
use crate::{bstr::BStr, revision::Spec, Repository};
mod types;
+use crate::bstr::BString;
pub use types::{Error, ObjectKindHint, Options, RefsHint};
///
@@ -45,6 +46,9 @@ impl<'repo> Spec<'repo> {
struct Delegate<'repo> {
refs: [Option<gix_ref::Reference>; 2],
objs: [Option<HashSet<ObjectId>>; 2],
+ /// Path specified like `@:<path>` or `:<path>` for later use when looking up specs.
+ /// Note that it terminates spec parsing, so it's either `0` or `1`, never both.
+ paths: [Option<(BString, gix_object::tree::EntryMode)>; 2],
/// The originally encountered ambiguous objects for potential later use in errors.
ambiguous_objects: [Option<HashSet<ObjectId>>; 2],
idx: usize,
diff --git a/vendor/gix/src/revision/walk.rs b/vendor/gix/src/revision/walk.rs
index 5e76ad898..19d15d569 100644
--- a/vendor/gix/src/revision/walk.rs
+++ b/vendor/gix/src/revision/walk.rs
@@ -1,5 +1,5 @@
use gix_hash::ObjectId;
-use gix_odb::FindExt;
+use gix_object::FindExt;
use crate::{ext::ObjectIdExt, revision, Repository};
@@ -169,7 +169,7 @@ impl<'repo> Platform<'repo> {
gix_traverse::commit::Ancestors::filtered(
tips,
gix_traverse::commit::ancestors::State::default(),
- move |oid, buf| repo.objects.find_commit_iter(oid, buf),
+ &repo.objects,
{
// Note that specific shallow handling for commit-graphs isn't needed as these contain
// all information there is, and exclude shallow parents to be structurally consistent.
diff --git a/vendor/gix/src/submodule/mod.rs b/vendor/gix/src/submodule/mod.rs
index 52c5938fc..fcfffd26f 100644
--- a/vendor/gix/src/submodule/mod.rs
+++ b/vendor/gix/src/submodule/mod.rs
@@ -7,7 +7,6 @@ use std::{
path::PathBuf,
};
-use gix_odb::FindExt;
pub use gix_submodule::*;
use crate::{bstr::BStr, repository::IndexPersistedOrInMemory, Repository, Submodule};
@@ -147,9 +146,7 @@ impl<'repo> Submodule<'repo> {
&mut |relative_path, case, is_dir, out| {
attributes
.set_case(case)
- .at_entry(relative_path, Some(is_dir), |id, buf| {
- self.state.repo.objects.find_blob(id, buf)
- })
+ .at_entry(relative_path, Some(is_dir), &self.state.repo.objects)
.map_or(false, |platform| platform.matching_attributes(out))
}
})?;
@@ -184,7 +181,7 @@ impl<'repo> Submodule<'repo> {
.head_commit()?
.tree()?
.peel_to_entry_by_path(gix_path::from_bstr(path.as_ref()))?
- .and_then(|entry| (entry.mode() == gix_object::tree::EntryMode::Commit).then_some(entry.inner.oid)))
+ .and_then(|entry| (entry.mode().is_commit()).then_some(entry.inner.oid)))
}
/// Return the path at which the repository of the submodule should be located.