diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-18 02:49:50 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-18 02:49:50 +0000 |
commit | 9835e2ae736235810b4ea1c162ca5e65c547e770 (patch) | |
tree | 3fcebf40ed70e581d776a8a4c65923e8ec20e026 /vendor/gix/src/repository | |
parent | Releasing progress-linux version 1.70.0+dfsg2-1~progress7.99u1. (diff) | |
download | rustc-9835e2ae736235810b4ea1c162ca5e65c547e770.tar.xz rustc-9835e2ae736235810b4ea1c162ca5e65c547e770.zip |
Merging upstream version 1.71.1+dfsg1.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'vendor/gix/src/repository')
-rw-r--r-- | vendor/gix/src/repository/attributes.rs | 50 | ||||
-rw-r--r-- | vendor/gix/src/repository/excludes.rs | 45 | ||||
-rw-r--r-- | vendor/gix/src/repository/impls.rs | 4 | ||||
-rw-r--r-- | vendor/gix/src/repository/init.rs | 3 | ||||
-rw-r--r-- | vendor/gix/src/repository/mod.rs | 4 | ||||
-rw-r--r-- | vendor/gix/src/repository/object.rs | 54 | ||||
-rw-r--r-- | vendor/gix/src/repository/permissions.rs | 119 | ||||
-rw-r--r-- | vendor/gix/src/repository/shallow.rs | 65 | ||||
-rw-r--r-- | vendor/gix/src/repository/worktree.rs | 24 |
9 files changed, 306 insertions, 62 deletions
diff --git a/vendor/gix/src/repository/attributes.rs b/vendor/gix/src/repository/attributes.rs new file mode 100644 index 000000000..252529761 --- /dev/null +++ b/vendor/gix/src/repository/attributes.rs @@ -0,0 +1,50 @@ +//! exclude information +use crate::Repository; + +impl Repository { + /// Configure a file-system cache for accessing git attributes *and* excludes on a per-path basis. + /// + /// Use `attribute_source` to specify where to read attributes from. Also note that exclude information will + /// always try to read `.gitignore` files from disk before trying to read it from the `index`. + /// + /// Note that no worktree is required for this to work, even though access to in-tree `.gitattributes` and `.gitignore` files + /// would require a non-empty `index` that represents a git tree. + /// + /// This takes into consideration all the usual repository configuration, namely: + /// + /// * `$XDG_CONFIG_HOME/…/ignore|attributes` if `core.excludesFile|attributesFile` is *not* set, otherwise use the configured file. + /// * `$GIT_DIR/info/exclude|attributes` if present. + // TODO: test, provide higher-level custom Cache wrapper that is much easier to use and doesn't panic when accessing entries + // by non-relative path. + pub fn attributes( + &self, + index: &gix_index::State, + attributes_source: gix_worktree::cache::state::attributes::Source, + ignore_source: gix_worktree::cache::state::ignore::Source, + exclude_overrides: Option<gix_ignore::Search>, + ) -> Result<gix_worktree::Cache, crate::attributes::Error> { + let case = if self.config.ignore_case { + gix_glob::pattern::Case::Fold + } else { + gix_glob::pattern::Case::Sensitive + }; + let (attributes, mut buf) = self.config.assemble_attribute_globals( + self.git_dir(), + attributes_source, + self.options.permissions.attributes, + )?; + let ignore = + self.config + .assemble_exclude_globals(self.git_dir(), exclude_overrides, ignore_source, &mut buf)?; + let state = gix_worktree::cache::State::AttributesAndIgnoreStack { attributes, ignore }; + let attribute_list = state.id_mappings_from_index(index, index.path_backing(), ignore_source, case); + Ok(gix_worktree::Cache::new( + // this is alright as we don't cause mutation of that directory, it's virtual. + self.work_dir().unwrap_or(self.git_dir()), + state, + case, + buf, + attribute_list, + )) + } +} diff --git a/vendor/gix/src/repository/excludes.rs b/vendor/gix/src/repository/excludes.rs new file mode 100644 index 000000000..6281549e0 --- /dev/null +++ b/vendor/gix/src/repository/excludes.rs @@ -0,0 +1,45 @@ +//! exclude information +use crate::{config, Repository}; +impl Repository { + /// Configure a file-system cache checking if files below the repository are excluded, reading `.gitignore` files from + /// the specified `source`. + /// + /// Note that no worktree is required for this to work, even though access to in-tree `.gitignore` files would require + /// a non-empty `index` that represents a tree with `.gitignore` files. + /// + /// This takes into consideration all the usual repository configuration, namely: + /// + /// * `$XDG_CONFIG_HOME/…/ignore` if `core.excludesFile` is *not* set, otherwise use the configured file. + /// * `$GIT_DIR/info/exclude` if present. + /// + /// When only excludes are desired, this is the most efficient way to obtain them. Otherwise use + /// [`Repository::attributes()`] for accessing both attributes and excludes. + // TODO: test, provide higher-level custom Cache wrapper that is much easier to use and doesn't panic when accessing entries + // by non-relative path. + pub fn excludes( + &self, + index: &gix_index::State, + overrides: Option<gix_ignore::Search>, + source: gix_worktree::cache::state::ignore::Source, + ) -> Result<gix_worktree::Cache, config::exclude_stack::Error> { + let case = if self.config.ignore_case { + gix_glob::pattern::Case::Fold + } else { + gix_glob::pattern::Case::Sensitive + }; + let mut buf = Vec::with_capacity(512); + let ignore = self + .config + .assemble_exclude_globals(self.git_dir(), overrides, source, &mut buf)?; + let state = gix_worktree::cache::State::IgnoreStack(ignore); + let attribute_list = state.id_mappings_from_index(index, index.path_backing(), source, case); + Ok(gix_worktree::Cache::new( + // this is alright as we don't cause mutation of that directory, it's virtual. + self.work_dir().unwrap_or(self.git_dir()), + state, + case, + buf, + attribute_list, + )) + } +} diff --git a/vendor/gix/src/repository/impls.rs b/vendor/gix/src/repository/impls.rs index 6cf2b2e9b..5da55290c 100644 --- a/vendor/gix/src/repository/impls.rs +++ b/vendor/gix/src/repository/impls.rs @@ -8,6 +8,7 @@ impl Clone for crate::Repository { self.config.clone(), self.options.clone(), self.index.clone(), + self.shallow_commits.clone(), ) } } @@ -40,6 +41,7 @@ impl From<&crate::ThreadSafeRepository> for crate::Repository { repo.config.clone(), repo.linked_worktree_options.clone(), repo.index.clone(), + repo.shallow_commits.clone(), ) } } @@ -54,6 +56,7 @@ impl From<crate::ThreadSafeRepository> for crate::Repository { repo.config, repo.linked_worktree_options, repo.index, + repo.shallow_commits, ) } } @@ -68,6 +71,7 @@ impl From<crate::Repository> for crate::ThreadSafeRepository { config: r.config, linked_worktree_options: r.options, index: r.index, + shallow_commits: r.shallow_commits, } } } diff --git a/vendor/gix/src/repository/init.rs b/vendor/gix/src/repository/init.rs index ae6a42c3b..16659a013 100644 --- a/vendor/gix/src/repository/init.rs +++ b/vendor/gix/src/repository/init.rs @@ -1,6 +1,7 @@ use std::cell::RefCell; impl crate::Repository { + #[allow(clippy::too_many_arguments)] pub(crate) fn from_refs_and_objects( refs: crate::RefStore, objects: crate::OdbHandle, @@ -9,6 +10,7 @@ impl crate::Repository { config: crate::config::Cache, linked_worktree_options: crate::open::Options, index: crate::worktree::IndexStorage, + shallow_commits: crate::shallow::CommitsStorage, ) -> Self { let objects = setup_objects(objects, &config); crate::Repository { @@ -20,6 +22,7 @@ impl crate::Repository { config, options: linked_worktree_options, index, + shallow_commits, } } diff --git a/vendor/gix/src/repository/mod.rs b/vendor/gix/src/repository/mod.rs index 31199e22d..5b7a70d3b 100644 --- a/vendor/gix/src/repository/mod.rs +++ b/vendor/gix/src/repository/mod.rs @@ -19,17 +19,19 @@ impl crate::Repository { } } +mod attributes; mod cache; mod config; +mod excludes; pub(crate) mod identity; mod impls; mod init; mod location; mod object; -pub(crate) mod permissions; mod reference; mod remote; mod revision; +mod shallow; mod snapshots; mod state; mod thread_safe; diff --git a/vendor/gix/src/repository/object.rs b/vendor/gix/src/repository/object.rs index bda1a54c3..f4592475f 100644 --- a/vendor/gix/src/repository/object.rs +++ b/vendor/gix/src/repository/object.rs @@ -1,5 +1,6 @@ #![allow(clippy::result_large_err)] use std::convert::TryInto; +use std::ops::DerefMut; use gix_hash::ObjectId; use gix_odb::{Find, FindExt, Write}; @@ -36,7 +37,7 @@ impl crate::Repository { Ok(Object::from_data(id, kind, buf, self)) } - /// Try to find the object with `id` or return `None` it it wasn't found. + /// Try to find the object with `id` or return `None` if it wasn't found. pub fn try_find_object(&self, id: impl Into<ObjectId>) -> Result<Option<Object<'_>>, object::find::Error> { let id = id.into(); if id == gix_hash::ObjectId::empty_tree(self.object_hash()) { @@ -58,32 +59,71 @@ impl crate::Repository { } } + fn shared_empty_buf(&self) -> std::cell::RefMut<'_, Vec<u8>> { + let mut bufs = self.bufs.borrow_mut(); + if bufs.last().is_none() { + bufs.push(Vec::with_capacity(512)); + } + std::cell::RefMut::map(bufs, |bufs| { + let buf = bufs.last_mut().expect("we assure one is present"); + buf.clear(); + buf + }) + } + /// Write the given object into the object database and return its object id. + /// + /// Note that we hash the object in memory to avoid storing objects that are already present. That way, + /// we avoid writing duplicate objects using slow disks that will eventually have to be garbage collected. pub fn write_object(&self, object: impl gix_object::WriteTo) -> Result<Id<'_>, object::write::Error> { + let mut buf = self.shared_empty_buf(); + object.write_to(buf.deref_mut())?; + + let oid = gix_object::compute_hash(self.object_hash(), object.kind(), &buf); + if self.objects.contains(oid) { + return Ok(oid.attach(self)); + } + self.objects - .write(object) + .write_buf(object.kind(), &buf) .map(|oid| oid.attach(self)) .map_err(Into::into) } /// Write a blob from the given `bytes`. + /// + /// We avoid writing duplicate objects to slow disks that will eventually have to be garbage collected by + /// pre-hashing the data, and checking if the object is already present. pub fn write_blob(&self, bytes: impl AsRef<[u8]>) -> Result<Id<'_>, object::write::Error> { + let bytes = bytes.as_ref(); + let oid = gix_object::compute_hash(self.object_hash(), gix_object::Kind::Blob, bytes); + if self.objects.contains(oid) { + return Ok(oid.attach(self)); + } self.objects - .write_buf(gix_object::Kind::Blob, bytes.as_ref()) + .write_buf(gix_object::Kind::Blob, bytes) .map(|oid| oid.attach(self)) } /// Write a blob from the given `Read` implementation. + /// + /// Note that we hash the object in memory to avoid storing objects that are already present. That way, + /// we avoid writing duplicate objects using slow disks that will eventually have to be garbage collected. + /// + /// If that is prohibitive, use the object database directly. pub fn write_blob_stream( &self, mut bytes: impl std::io::Read + std::io::Seek, ) -> Result<Id<'_>, object::write::Error> { - let current = bytes.stream_position()?; - let len = bytes.seek(std::io::SeekFrom::End(0))? - current; - bytes.seek(std::io::SeekFrom::Start(current))?; + let mut buf = self.shared_empty_buf(); + std::io::copy(&mut bytes, buf.deref_mut())?; + let oid = gix_object::compute_hash(self.object_hash(), gix_object::Kind::Blob, &buf); + if self.objects.contains(oid) { + return Ok(oid.attach(self)); + } self.objects - .write_stream(gix_object::Kind::Blob, len, bytes) + .write_buf(gix_object::Kind::Blob, &buf) .map(|oid| oid.attach(self)) } diff --git a/vendor/gix/src/repository/permissions.rs b/vendor/gix/src/repository/permissions.rs index 88b61b739..633575a9d 100644 --- a/vendor/gix/src/repository/permissions.rs +++ b/vendor/gix/src/repository/permissions.rs @@ -1,14 +1,7 @@ +//! Various permissions to define what can be done when operating a [`Repository`][crate::Repository]. +use crate::open::Permissions; use gix_sec::Trust; -/// Permissions associated with various resources of a git repository -#[derive(Debug, Clone)] -pub struct Permissions { - /// Permissions related to the environment - pub env: Environment, - /// Permissions related to the handling of git configuration. - pub config: Config, -} - /// Configure from which sources git configuration may be loaded. /// /// Note that configuration from inside of the repository is always loaded as it's definitely required for correctness. @@ -17,7 +10,7 @@ pub struct Config { /// The git binary may come with configuration as part of its configuration, and if this is true (default false) /// we will load the configuration of the git binary, if present and not a duplicate of the ones below. /// - /// It's disable by default as it involves executing the git binary once per execution of the application. + /// It's disabled by default as it may involve executing the git binary once per execution of the application. pub git_binary: bool, /// Whether to use the system configuration. /// This is defined as `$(prefix)/etc/gitconfig` on unix. @@ -50,6 +43,18 @@ impl Config { includes: true, } } + + /// Load only configuration local to the git repository. + pub fn isolated() -> Self { + Config { + git_binary: false, + system: false, + git: false, + user: false, + env: false, + includes: false, + } + } } impl Default for Config { @@ -58,8 +63,55 @@ impl Default for Config { } } +/// Configure from which `gitattribute` files may be loaded. +/// +/// Note that `.gitattribute` files from within the repository are always loaded. +#[derive(Copy, Clone, Ord, PartialOrd, PartialEq, Eq, Debug, Hash)] +pub struct Attributes { + /// The git binary may come with attribute configuration in its installation directory, and if this is true (default false) + /// we will load the configuration of the git binary. + /// + /// It's disabled by default as it involves executing the git binary once per execution of the application. + pub git_binary: bool, + /// Whether to use the system configuration. + /// This is typically defined as `$(prefix)/etc/gitconfig`. + pub system: bool, + /// Whether to use the git application configuration. + /// + /// A platform defined location for where a user's git application configuration should be located. + /// If `$XDG_CONFIG_HOME` is not set or empty, `$HOME/.config/git/attributes` will be used + /// on unix. + pub git: bool, +} + +impl Attributes { + /// Allow everything which usually relates to a fully trusted environment + pub fn all() -> Self { + Attributes { + git_binary: false, + system: true, + git: true, + } + } + + /// Allow loading attributes that are local to the git repository. + pub fn isolated() -> Self { + Attributes { + git_binary: false, + system: false, + git: false, + } + } +} + +impl Default for Attributes { + fn default() -> Self { + Self::all() + } +} + /// Permissions related to the usage of environment variables -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Copy)] pub struct Environment { /// Control whether resources pointed to by `XDG_CONFIG_HOME` can be used when looking up common configuration values. /// @@ -101,18 +153,29 @@ impl Environment { objects: allow, } } + + /// Don't allow loading any environment variables. + pub fn isolated() -> Self { + let deny = gix_sec::Permission::Deny; + Environment { + xdg_config_home: deny, + home: deny, + ssh_prefix: deny, + git_prefix: deny, + http_transport: deny, + identity: deny, + objects: deny, + } + } } impl Permissions { - /// Return permissions that will not include configuration files not owned by the current user, - /// but trust system and global configuration files along with those which are owned by the current user. - /// - /// This allows to read and write repositories even if they aren't owned by the current user, but avoid using - /// anything else that could cause us to write into unknown locations or use programs beyond our `PATH`. + /// Secure permissions are similar to `all()` pub fn secure() -> Self { Permissions { env: Environment::all(), config: Config::all(), + attributes: Attributes::all(), } } @@ -122,32 +185,16 @@ impl Permissions { Permissions { env: Environment::all(), config: Config::all(), + attributes: Attributes::all(), } } /// Don't read any but the local git configuration and deny reading any environment variables. pub fn isolated() -> Self { Permissions { - config: Config { - git_binary: false, - system: false, - git: false, - user: false, - env: false, - includes: false, - }, - env: { - let deny = gix_sec::Permission::Deny; - Environment { - xdg_config_home: deny, - home: deny, - ssh_prefix: deny, - git_prefix: deny, - http_transport: deny, - identity: deny, - objects: deny, - } - }, + config: Config::isolated(), + attributes: Attributes::isolated(), + env: Environment::isolated(), } } } diff --git a/vendor/gix/src/repository/shallow.rs b/vendor/gix/src/repository/shallow.rs new file mode 100644 index 000000000..7fac83a55 --- /dev/null +++ b/vendor/gix/src/repository/shallow.rs @@ -0,0 +1,65 @@ +use std::{borrow::Cow, path::PathBuf}; + +use crate::{ + bstr::ByteSlice, + config::tree::{gitoxide, Key}, + Repository, +}; + +impl Repository { + /// Return `true` if the repository is a shallow clone, i.e. contains history only up to a certain depth. + pub fn is_shallow(&self) -> bool { + self.shallow_file() + .metadata() + .map_or(false, |m| m.is_file() && m.len() > 0) + } + + /// Return a shared list of shallow commits which is updated automatically if the in-memory snapshot has become stale + /// as the underlying file on disk has changed. + /// + /// The list of shallow commits represents the shallow boundary, beyond which we are lacking all (parent) commits. + /// Note that the list is never empty, as `Ok(None)` is returned in that case indicating the repository + /// isn't a shallow clone. + /// + /// The shared list is shared across all clones of this repository. + pub fn shallow_commits(&self) -> Result<Option<crate::shallow::Commits>, crate::shallow::open::Error> { + self.shallow_commits.recent_snapshot( + || self.shallow_file().metadata().ok().and_then(|m| m.modified().ok()), + || { + let buf = match std::fs::read(self.shallow_file()) { + Ok(buf) => buf, + Err(err) if err.kind() == std::io::ErrorKind::NotFound => return Ok(None), + Err(err) => return Err(err.into()), + }; + + let mut commits = buf + .lines() + .map(gix_hash::ObjectId::from_hex) + .collect::<Result<Vec<_>, _>>()?; + + commits.sort(); + if commits.is_empty() { + Ok(None) + } else { + Ok(Some(commits)) + } + }, + ) + } + + /// Return the path to the `shallow` file which contains hashes, one per line, that describe commits that don't have their + /// parents within this repository. + /// + /// Note that it may not exist if the repository isn't actually shallow. + pub fn shallow_file(&self) -> PathBuf { + let shallow_name = self + .config + .resolved + .string_filter_by_key( + gitoxide::Core::SHALLOW_FILE.logical_name().as_str(), + &mut self.filter_config_section(), + ) + .unwrap_or_else(|| Cow::Borrowed("shallow".into())); + self.common_dir().join(gix_path::from_bstr(shallow_name)) + } +} diff --git a/vendor/gix/src/repository/worktree.rs b/vendor/gix/src/repository/worktree.rs index 2de31bc86..316009d29 100644 --- a/vendor/gix/src/repository/worktree.rs +++ b/vendor/gix/src/repository/worktree.rs @@ -1,6 +1,7 @@ +use crate::config::cache::util::ApplyLeniencyDefault; use crate::{worktree, Worktree}; -/// Worktree iteration +/// Interact with individual worktrees and their information. impl crate::Repository { /// Return a list of all _linked_ worktrees sorted by private git dir path as a lightweight proxy. /// @@ -25,10 +26,6 @@ impl crate::Repository { res.sort_by(|a, b| a.git_dir.cmp(&b.git_dir)); Ok(res) } -} - -/// Interact with individual worktrees and their information. -impl crate::Repository { /// Return the repository owning the main worktree, typically from a linked worktree. /// /// Note that it might be the one that is currently open if this repository doesn't point to a linked worktree. @@ -58,23 +55,14 @@ impl crate::Repository { /// /// It will use the `index.threads` configuration key to learn how many threads to use. /// Note that it may fail if there is no index. - // TODO: test pub fn open_index(&self) -> Result<gix_index::File, worktree::open_index::Error> { let thread_limit = self .config .resolved - .boolean("index", None, "threads") - .map(|res| { - res.map(|value| usize::from(!value)).or_else(|err| { - gix_config::Integer::try_from(err.input.as_ref()) - .map_err(|err| worktree::open_index::Error::ConfigIndexThreads { - value: err.input.clone(), - err, - }) - .map(|value| value.to_decimal().and_then(|v| v.try_into().ok()).unwrap_or(1)) - }) - }) - .transpose()?; + .string("index", None, "threads") + .map(|value| crate::config::tree::Index::THREADS.try_into_index_threads(value)) + .transpose() + .with_lenient_default(self.config.lenient_config)?; gix_index::File::at( self.index_path(), self.object_hash(), |