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( index: &mut gix_index::State, dir: impl Into, find: Find, files: &mut impl Progress, bytes: &mut impl Progress, should_interrupt: &AtomicBool, options: crate::checkout::Options, ) -> Result> where Find: for<'a> FnMut(&oid, &'a mut Vec) -> Result, 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( index: &mut gix_index::State, paths: &gix_index::PathStorage, dir: impl Into, find: Find, files: &mut impl Progress, bytes: &mut impl Progress, should_interrupt: &AtomicBool, options: crate::checkout::Options, ) -> Result> where Find: for<'a> FnMut(&oid, &'a mut Vec) -> Result, 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, }) }