summaryrefslogtreecommitdiffstats
path: root/vendor/gix-worktree/src/checkout
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/gix-worktree/src/checkout')
-rw-r--r--vendor/gix-worktree/src/checkout/chunk.rs182
-rw-r--r--vendor/gix-worktree/src/checkout/entry.rs166
-rw-r--r--vendor/gix-worktree/src/checkout/function.rs119
-rw-r--r--vendor/gix-worktree/src/checkout/mod.rs77
4 files changed, 0 insertions, 544 deletions
diff --git a/vendor/gix-worktree/src/checkout/chunk.rs b/vendor/gix-worktree/src/checkout/chunk.rs
deleted file mode 100644
index 9de9e424e..000000000
--- a/vendor/gix-worktree/src/checkout/chunk.rs
+++ /dev/null
@@ -1,182 +0,0 @@
-use std::sync::atomic::{AtomicUsize, Ordering};
-
-use bstr::BStr;
-use gix_features::progress::Progress;
-use gix_hash::oid;
-
-use crate::{checkout, checkout::entry, Cache};
-
-mod reduce {
- use std::{
- marker::PhantomData,
- sync::atomic::{AtomicUsize, Ordering},
- };
-
- use gix_features::progress::Progress;
-
- use crate::checkout;
-
- pub struct Reduce<'a, 'entry, P1, P2, E> {
- pub files: &'a mut P1,
- pub bytes: &'a mut P2,
- pub num_files: &'a AtomicUsize,
- pub aggregate: super::Outcome<'entry>,
- pub marker: PhantomData<E>,
- }
-
- impl<'a, 'entry, P1, P2, E> gix_features::parallel::Reduce for Reduce<'a, 'entry, P1, P2, E>
- where
- P1: Progress,
- P2: Progress,
- E: std::error::Error + Send + Sync + 'static,
- {
- type Input = Result<super::Outcome<'entry>, checkout::Error<E>>;
- type FeedProduce = ();
- type Output = super::Outcome<'entry>;
- type Error = checkout::Error<E>;
-
- fn feed(&mut self, item: Self::Input) -> Result<Self::FeedProduce, Self::Error> {
- let item = item?;
- let super::Outcome {
- bytes_written,
- delayed,
- errors,
- collisions,
- } = item;
- self.aggregate.bytes_written += bytes_written;
- self.aggregate.delayed.extend(delayed);
- self.aggregate.errors.extend(errors);
- self.aggregate.collisions.extend(collisions);
-
- self.bytes.set(self.aggregate.bytes_written as usize);
- self.files.set(self.num_files.load(Ordering::Relaxed));
-
- Ok(())
- }
-
- fn finalize(self) -> Result<Self::Output, Self::Error> {
- Ok(self.aggregate)
- }
- }
-}
-pub use reduce::Reduce;
-
-#[derive(Default)]
-pub struct Outcome<'a> {
- pub collisions: Vec<checkout::Collision>,
- pub errors: Vec<checkout::ErrorRecord>,
- pub delayed: Vec<(&'a mut gix_index::Entry, &'a BStr)>,
- pub bytes_written: u64,
-}
-
-#[derive(Clone)]
-pub struct Context<'a, Find: Clone> {
- pub find: Find,
- pub path_cache: Cache,
- pub buf: Vec<u8>,
- pub options: checkout::Options,
- /// We keep these shared so that there is the chance for printing numbers that aren't looking like
- /// multiple of chunk sizes. Purely cosmetic. Otherwise it's the same as `files`.
- pub num_files: &'a AtomicUsize,
-}
-
-pub fn process<'entry, Find, E>(
- entries_with_paths: impl Iterator<Item = (&'entry mut gix_index::Entry, &'entry BStr)>,
- files: &mut impl Progress,
- bytes: &mut impl Progress,
- ctx: &mut Context<'_, Find>,
-) -> Result<Outcome<'entry>, checkout::Error<E>>
-where
- Find: for<'a> FnMut(&oid, &'a mut Vec<u8>) -> Result<gix_object::BlobRef<'a>, E> + Clone,
- E: std::error::Error + Send + Sync + 'static,
-{
- let mut delayed = Vec::new();
- let mut collisions = Vec::new();
- let mut errors = Vec::new();
- let mut bytes_written = 0;
-
- for (entry, entry_path) in entries_with_paths {
- // TODO: write test for that
- if entry.flags.contains(gix_index::entry::Flags::SKIP_WORKTREE) {
- files.inc();
- continue;
- }
-
- // Symlinks always have to be delayed on windows as they have to point to something that exists on creation.
- // And even if not, there is a distinction between file and directory symlinks, hence we have to check what the target is
- // before creating it.
- // And to keep things sane, we just do the same on non-windows as well which is similar to what git does and adds some safety
- // around writing through symlinks (even though we handle this).
- // This also means that we prefer content in files over symlinks in case of collisions, which probably is for the better, too.
- if entry.mode == gix_index::entry::Mode::SYMLINK {
- delayed.push((entry, entry_path));
- continue;
- }
-
- bytes_written +=
- checkout_entry_handle_result(entry, entry_path, &mut errors, &mut collisions, files, bytes, ctx)? as u64;
- }
-
- Ok(Outcome {
- bytes_written,
- errors,
- collisions,
- delayed,
- })
-}
-
-pub fn checkout_entry_handle_result<Find, E>(
- entry: &mut gix_index::Entry,
- entry_path: &BStr,
- errors: &mut Vec<checkout::ErrorRecord>,
- collisions: &mut Vec<checkout::Collision>,
- files: &mut impl Progress,
- bytes: &mut impl Progress,
- Context {
- find,
- path_cache,
- buf,
- options,
- num_files,
- }: &mut Context<'_, Find>,
-) -> Result<usize, checkout::Error<E>>
-where
- Find: for<'a> FnMut(&oid, &'a mut Vec<u8>) -> Result<gix_object::BlobRef<'a>, E> + Clone,
- E: std::error::Error + Send + Sync + 'static,
-{
- let res = entry::checkout(
- entry,
- entry_path,
- entry::Context { find, path_cache, buf },
- options.clone(),
- );
- files.inc();
- num_files.fetch_add(1, Ordering::SeqCst);
- match res {
- Ok(object_size) => {
- bytes.inc_by(object_size);
- Ok(object_size)
- }
- Err(checkout::Error::Io(err)) if gix_fs::symlink::is_collision_error(&err) => {
- // We are here because a file existed or was blocked by a directory which shouldn't be possible unless
- // we are on a file insensitive file system.
- files.fail(format!("{}: collided ({:?})", entry_path, err.kind()));
- collisions.push(checkout::Collision {
- path: entry_path.into(),
- error_kind: err.kind(),
- });
- Ok(0)
- }
- Err(err) => {
- if options.keep_going {
- errors.push(checkout::ErrorRecord {
- path: entry_path.into(),
- error: Box::new(err),
- });
- Ok(0)
- } else {
- Err(err)
- }
- }
- }
-}
diff --git a/vendor/gix-worktree/src/checkout/entry.rs b/vendor/gix-worktree/src/checkout/entry.rs
deleted file mode 100644
index 524cf90f2..000000000
--- a/vendor/gix-worktree/src/checkout/entry.rs
+++ /dev/null
@@ -1,166 +0,0 @@
-use std::{fs::OpenOptions, io::Write, path::Path};
-
-use bstr::BStr;
-use gix_hash::oid;
-use gix_index::{entry::Stat, Entry};
-use io_close::Close;
-
-use crate::Cache;
-
-pub struct Context<'a, Find> {
- pub find: &'a mut Find,
- pub path_cache: &'a mut Cache,
- pub buf: &'a mut Vec<u8>,
-}
-
-#[cfg_attr(not(unix), allow(unused_variables))]
-pub fn checkout<Find, E>(
- entry: &mut Entry,
- entry_path: &BStr,
- Context { find, path_cache, buf }: Context<'_, Find>,
- crate::checkout::Options {
- fs: gix_fs::Capabilities {
- symlink,
- executable_bit,
- ..
- },
- destination_is_initially_empty,
- overwrite_existing,
- ..
- }: crate::checkout::Options,
-) -> Result<usize, crate::checkout::Error<E>>
-where
- Find: for<'a> FnMut(&oid, &'a mut Vec<u8>) -> Result<gix_object::BlobRef<'a>, E>,
- E: std::error::Error + Send + Sync + 'static,
-{
- let dest_relative = gix_path::try_from_bstr(entry_path).map_err(|_| crate::checkout::Error::IllformedUtf8 {
- path: entry_path.to_owned(),
- })?;
- let is_dir = Some(entry.mode == gix_index::entry::Mode::COMMIT || entry.mode == gix_index::entry::Mode::DIR);
- let dest = path_cache.at_path(dest_relative, is_dir, &mut *find)?.path();
-
- let object_size = match entry.mode {
- gix_index::entry::Mode::FILE | gix_index::entry::Mode::FILE_EXECUTABLE => {
- let obj = find(&entry.id, buf).map_err(|err| crate::checkout::Error::Find {
- err,
- oid: entry.id,
- path: dest.to_path_buf(),
- })?;
-
- #[cfg_attr(not(unix), allow(unused_mut))]
- let mut options = open_options(dest, destination_is_initially_empty, overwrite_existing);
- let needs_executable_bit = executable_bit && entry.mode == gix_index::entry::Mode::FILE_EXECUTABLE;
- #[cfg(unix)]
- if needs_executable_bit && destination_is_initially_empty {
- use std::os::unix::fs::OpenOptionsExt;
- // Note that these only work if the file was newly created, but won't if it's already
- // existing, possibly without the executable bit set. Thus we do this only if the file is new.
- options.mode(0o777);
- }
-
- let mut file = try_write_or_unlink(dest, overwrite_existing, |p| options.open(p))?;
- file.write_all(obj.data)?;
-
- // For possibly existing, overwritten files, we must change the file mode explicitly.
- #[cfg(unix)]
- if needs_executable_bit && !destination_is_initially_empty {
- use std::os::unix::fs::PermissionsExt;
- let mut perm = std::fs::symlink_metadata(dest)?.permissions();
- perm.set_mode(0o777);
- std::fs::set_permissions(dest, perm)?;
- }
- // NOTE: we don't call `file.sync_all()` here knowing that some filesystems don't handle this well.
- // revisit this once there is a bug to fix.
- entry.stat = Stat::from_fs(&file.metadata()?)?;
- file.close()?;
- obj.data.len()
- }
- gix_index::entry::Mode::SYMLINK => {
- let obj = find(&entry.id, buf).map_err(|err| crate::checkout::Error::Find {
- err,
- oid: entry.id,
- path: dest.to_path_buf(),
- })?;
- let symlink_destination = gix_path::try_from_byte_slice(obj.data)
- .map_err(|_| crate::checkout::Error::IllformedUtf8 { path: obj.data.into() })?;
-
- if symlink {
- try_write_or_unlink(dest, overwrite_existing, |p| {
- gix_fs::symlink::create(symlink_destination, p)
- })?;
- } else {
- let mut file = try_write_or_unlink(dest, overwrite_existing, |p| {
- open_options(p, destination_is_initially_empty, overwrite_existing).open(dest)
- })?;
- file.write_all(obj.data)?;
- file.close()?;
- }
-
- entry.stat = Stat::from_fs(&std::fs::symlink_metadata(dest)?)?;
- obj.data.len()
- }
- gix_index::entry::Mode::DIR => todo!(),
- gix_index::entry::Mode::COMMIT => todo!(),
- _ => unreachable!(),
- };
- Ok(object_size)
-}
-
-/// Note that this works only because we assume to not race ourselves when symlinks are involved, and we do this by
-/// delaying symlink creation to the end and will always do that sequentially.
-/// It's still possible to fall for a race if other actors create symlinks in our path, but that's nothing to defend against.
-fn try_write_or_unlink<T>(
- path: &Path,
- overwrite_existing: bool,
- op: impl Fn(&Path) -> std::io::Result<T>,
-) -> std::io::Result<T> {
- if overwrite_existing {
- match op(path) {
- Ok(res) => Ok(res),
- Err(err) if gix_fs::symlink::is_collision_error(&err) => {
- try_unlink_path_recursively(path, &std::fs::symlink_metadata(path)?)?;
- op(path)
- }
- Err(err) => Err(err),
- }
- } else {
- op(path)
- }
-}
-
-fn try_unlink_path_recursively(path: &Path, path_meta: &std::fs::Metadata) -> std::io::Result<()> {
- if path_meta.is_dir() {
- std::fs::remove_dir_all(path)
- } else if path_meta.file_type().is_symlink() {
- gix_fs::symlink::remove(path)
- } else {
- std::fs::remove_file(path)
- }
-}
-
-#[cfg(not(debug_assertions))]
-fn debug_assert_dest_is_no_symlink(_path: &Path) {}
-
-/// This is a debug assertion as we expect the machinery calling this to prevent this possibility in the first place
-#[cfg(debug_assertions)]
-fn debug_assert_dest_is_no_symlink(path: &Path) {
- if let Ok(meta) = path.metadata() {
- debug_assert!(
- !meta.file_type().is_symlink(),
- "BUG: should not ever allow to overwrite/write-into the target of a symbolic link: {}",
- path.display()
- );
- }
-}
-
-fn open_options(path: &Path, destination_is_initially_empty: bool, overwrite_existing: bool) -> OpenOptions {
- if overwrite_existing || !destination_is_initially_empty {
- debug_assert_dest_is_no_symlink(path);
- }
- let mut options = gix_features::fs::open_options_no_follow();
- options
- .create_new(destination_is_initially_empty && !overwrite_existing)
- .create(!destination_is_initially_empty || overwrite_existing)
- .write(true);
- options
-}
diff --git a/vendor/gix-worktree/src/checkout/function.rs b/vendor/gix-worktree/src/checkout/function.rs
deleted file mode 100644
index 8e69fd4d6..000000000
--- a/vendor/gix-worktree/src/checkout/function.rs
+++ /dev/null
@@ -1,119 +0,0 @@
-use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
-
-use gix_features::{interrupt, parallel::in_parallel, progress, progress::Progress};
-use gix_hash::oid;
-
-use crate::{cache, checkout::chunk, Cache};
-
-/// Note that interruption still produce an `Ok(…)` value, so the caller should look at `should_interrupt` to communicate the outcome.
-/// `dir` is the directory into which to checkout the `index`.
-/// `git_dir` is the `.git` directory for reading additional per-repository configuration files.
-#[allow(clippy::too_many_arguments)]
-pub fn checkout<Find, E>(
- index: &mut gix_index::State,
- dir: impl Into<std::path::PathBuf>,
- find: Find,
- files: &mut impl Progress,
- bytes: &mut impl Progress,
- should_interrupt: &AtomicBool,
- options: crate::checkout::Options,
-) -> Result<crate::checkout::Outcome, crate::checkout::Error<E>>
-where
- Find: for<'a> FnMut(&oid, &'a mut Vec<u8>) -> Result<gix_object::BlobRef<'a>, E> + Send + Clone,
- E: std::error::Error + Send + Sync + 'static,
-{
- let paths = index.take_path_backing();
- let res = checkout_inner(index, &paths, dir, find, files, bytes, should_interrupt, options);
- index.return_path_backing(paths);
- res
-}
-
-#[allow(clippy::too_many_arguments)]
-fn checkout_inner<Find, E>(
- index: &mut gix_index::State,
- paths: &gix_index::PathStorage,
- dir: impl Into<std::path::PathBuf>,
- find: Find,
- files: &mut impl Progress,
- bytes: &mut impl Progress,
- should_interrupt: &AtomicBool,
- options: crate::checkout::Options,
-) -> Result<crate::checkout::Outcome, crate::checkout::Error<E>>
-where
- Find: for<'a> FnMut(&oid, &'a mut Vec<u8>) -> Result<gix_object::BlobRef<'a>, E> + Send + Clone,
- E: std::error::Error + Send + Sync + 'static,
-{
- let num_files = AtomicUsize::default();
- let dir = dir.into();
- let case = if options.fs.ignore_case {
- gix_glob::pattern::Case::Fold
- } else {
- gix_glob::pattern::Case::Sensitive
- };
- let (chunk_size, thread_limit, num_threads) = gix_features::parallel::optimize_chunk_size_and_thread_limit(
- 100,
- index.entries().len().into(),
- options.thread_limit,
- None,
- );
-
- let state = cache::State::for_checkout(options.overwrite_existing, options.attributes.clone());
- let attribute_files = state.id_mappings_from_index(index, paths, Default::default(), case);
- let mut ctx = chunk::Context {
- buf: Vec::new(),
- path_cache: Cache::new(dir, state, case, Vec::with_capacity(512), attribute_files),
- find,
- options,
- num_files: &num_files,
- };
-
- let chunk::Outcome {
- mut collisions,
- mut errors,
- mut bytes_written,
- delayed,
- } = if num_threads == 1 {
- let entries_with_paths = interrupt::Iter::new(index.entries_mut_with_paths_in(paths), should_interrupt);
- chunk::process(entries_with_paths, files, bytes, &mut ctx)?
- } else {
- let entries_with_paths = interrupt::Iter::new(index.entries_mut_with_paths_in(paths), should_interrupt);
- in_parallel(
- gix_features::iter::Chunks {
- inner: entries_with_paths,
- size: chunk_size,
- },
- thread_limit,
- {
- let ctx = ctx.clone();
- move |_| (progress::Discard, progress::Discard, ctx.clone())
- },
- |chunk, (files, bytes, ctx)| chunk::process(chunk.into_iter(), files, bytes, ctx),
- chunk::Reduce {
- files,
- bytes,
- num_files: &num_files,
- aggregate: Default::default(),
- marker: Default::default(),
- },
- )?
- };
-
- for (entry, entry_path) in delayed {
- bytes_written += chunk::checkout_entry_handle_result(
- entry,
- entry_path,
- &mut errors,
- &mut collisions,
- files,
- bytes,
- &mut ctx,
- )? as u64;
- }
-
- Ok(crate::checkout::Outcome {
- files_updated: num_files.load(Ordering::Relaxed),
- collisions,
- errors,
- bytes_written,
- })
-}
diff --git a/vendor/gix-worktree/src/checkout/mod.rs b/vendor/gix-worktree/src/checkout/mod.rs
deleted file mode 100644
index 11f39b1b2..000000000
--- a/vendor/gix-worktree/src/checkout/mod.rs
+++ /dev/null
@@ -1,77 +0,0 @@
-#![allow(missing_docs)]
-
-use bstr::BString;
-use gix_index::entry::stat;
-
-#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
-pub struct Collision {
- /// the path that collided with something already present on disk.
- pub path: BString,
- /// The io error we encountered when checking out `path`.
- pub error_kind: std::io::ErrorKind,
-}
-
-pub struct ErrorRecord {
- /// the path that encountered the error.
- pub path: BString,
- /// The error
- pub error: Box<dyn std::error::Error + Send + Sync + 'static>,
-}
-
-#[derive(Default)]
-pub struct Outcome {
- /// The amount of files updated, or created.
- pub files_updated: usize,
- /// The amount of bytes written to disk,
- pub bytes_written: u64,
- pub collisions: Vec<Collision>,
- pub errors: Vec<ErrorRecord>,
-}
-
-#[derive(Clone, Default)]
-pub struct Options {
- /// capabilities of the file system
- pub fs: gix_fs::Capabilities,
- /// If set, don't use more than this amount of threads.
- /// Otherwise, usually use as many threads as there are logical cores.
- /// A value of 0 is interpreted as no-limit
- pub thread_limit: Option<usize>,
- /// If true, we assume no file to exist in the target directory, and want exclusive access to it.
- /// This should be enabled when cloning to avoid checks for freshness of files. This also enables
- /// detection of collisions based on whether or not exclusive file creation succeeds or fails.
- pub destination_is_initially_empty: bool,
- /// If true, default false, worktree entries on disk will be overwritten with content from the index
- /// even if they appear to be changed. When creating directories that clash with existing worktree entries,
- /// these will try to delete the existing entry.
- /// This is similar in behaviour as `git checkout --force`.
- pub overwrite_existing: bool,
- /// If true, default false, try to checkout as much as possible and don't abort on first error which isn't
- /// due to a conflict.
- /// The checkout operation will never fail, but count the encountered errors instead along with their paths.
- pub keep_going: bool,
- /// Control how stat comparisons are made when checking if a file is fresh.
- pub stat_options: stat::Options,
- /// A stack of attributes to use with the filesystem cache to use as driver for filters.
- pub attributes: crate::cache::state::Attributes,
-}
-
-#[derive(Debug, thiserror::Error)]
-pub enum Error<E: std::error::Error + Send + Sync + 'static> {
- #[error("Could not convert path to UTF8: {}", .path)]
- IllformedUtf8 { path: BString },
- #[error("The clock was off when reading file related metadata after updating a file on disk")]
- Time(#[from] std::time::SystemTimeError),
- #[error("IO error while writing blob or reading file metadata or changing filetype")]
- Io(#[from] std::io::Error),
- #[error("object {} for checkout at {} could not be retrieved from object database", .oid.to_hex(), .path.display())]
- Find {
- #[source]
- err: E,
- oid: gix_hash::ObjectId,
- path: std::path::PathBuf,
- },
-}
-
-mod chunk;
-mod entry;
-pub(crate) mod function;