Materialize the working copy using parallel file processing. This is an optimized version of `materialize` that: 1. Buffers each file's content in memory (single allocation per file) 2. Processes files in parallel using rayon 3. Writes each file to disk in a single `fs::write` call 4. Computes content hashes in-memory (no read-back pass) Falls back to sequential processing for files that fail in
(
&self,
only_paths: Option<std::collections::HashSet<String>>,
)
| 339 | /// Falls back to sequential processing for files that fail in the |
| 340 | /// parallel path. |
| 341 | pub fn materialize_parallel( |
| 342 | &self, |
| 343 | only_paths: Option<std::collections::HashSet<String>>, |
| 344 | ) -> Result<MaterializeResult, RepositoryError> { |
| 345 | use atomic_core::output::repo::{ |
| 346 | collect_children, FileOutputOptions, MaterializeOptions, OutputItem, |
| 347 | }; |
| 348 | use atomic_core::output::RetrieveOptions; |
| 349 | use rayon::prelude::*; |
| 350 | use std::collections::HashSet as StdHashSet; |
| 351 | |
| 352 | let txn = self |
| 353 | .pristine |
| 354 | .read_txn() |
| 355 | .map_err(|e| RepositoryError::Database(e.to_string()))?; |
| 356 | |
| 357 | let view = txn |
| 358 | .get_view(&self.current_view) |
| 359 | .map_err(|e| RepositoryError::Database(e.to_string()))? |
| 360 | .ok_or_else(|| RepositoryError::ViewNotFound { |
| 361 | name: self.current_view.clone(), |
| 362 | })?; |
| 363 | |
| 364 | let change_filter = collect_visible_change_ids(&txn, &view)?; |
| 365 | let change_filter_arc = Arc::new(change_filter); |
| 366 | |
| 367 | let options = MaterializeOptions::new().with_change_filter_arc(change_filter_arc.clone()); |
| 368 | |
| 369 | // Phase 1: Collect all items from the tree |
| 370 | let items = collect_children(&txn, Inode::ROOT, "", &options) |
| 371 | .map_err(|e| RepositoryError::Database(e.to_string()))?; |
| 372 | |
| 373 | let _file_options = FileOutputOptions::new(); |
| 374 | |
| 375 | // Phase 2+4: Filter files by view membership |
| 376 | let file_items: Vec<&OutputItem> = items |
| 377 | .iter() |
| 378 | .filter(|item| { |
| 379 | if item.is_directory { |
| 380 | return false; |
| 381 | } |
| 382 | if !options.matches_prefix(&item.path) { |
| 383 | return false; |
| 384 | } |
| 385 | // View-aware filter: skip files whose introducing change |
| 386 | // is not in the visible change set |
| 387 | if let Some(ref filter) = options.change_filter { |
| 388 | if !item.position.change.is_root() && !filter.contains(&item.position.change) { |
| 389 | return false; |
| 390 | } |
| 391 | } |
| 392 | // Selective materialize: skip files not in the explicit path set |
| 393 | if let Some(ref paths) = only_paths { |
| 394 | if !paths.contains(&item.path) { |
| 395 | return false; |
| 396 | } |
| 397 | } |
| 398 | true |
no test coverage detected