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>>,
)
| 413 | /// Falls back to sequential processing for files that fail in the |
| 414 | /// parallel path. |
| 415 | pub fn materialize_parallel( |
| 416 | &self, |
| 417 | only_paths: Option<std::collections::HashSet<String>>, |
| 418 | ) -> Result<MaterializeResult, RepositoryError> { |
| 419 | use atomic_core::output::repo::{ |
| 420 | collect_children, FileOutputOptions, MaterializeOptions, OutputItem, |
| 421 | }; |
| 422 | use atomic_core::output::RetrieveOptions; |
| 423 | use rayon::prelude::*; |
| 424 | use std::collections::HashSet as StdHashSet; |
| 425 | |
| 426 | let txn = self |
| 427 | .pristine |
| 428 | .read_txn() |
| 429 | .map_err(|e| RepositoryError::Database(e.to_string()))?; |
| 430 | |
| 431 | let view = txn |
| 432 | .get_view(&self.current_view) |
| 433 | .map_err(|e| RepositoryError::Database(e.to_string()))? |
| 434 | .ok_or_else(|| RepositoryError::ViewNotFound { |
| 435 | name: self.current_view.clone(), |
| 436 | })?; |
| 437 | |
| 438 | let change_filter = collect_visible_change_ids(&txn, &view)?; |
| 439 | let change_filter_arc = Arc::new(change_filter); |
| 440 | |
| 441 | let options = MaterializeOptions::new().with_change_filter_arc(change_filter_arc.clone()); |
| 442 | |
| 443 | // Phase 1: Collect all items from the tree |
| 444 | let items = collect_children(&txn, Inode::ROOT, "", &options) |
| 445 | .map_err(|e| RepositoryError::Database(e.to_string()))?; |
| 446 | |
| 447 | let _file_options = FileOutputOptions::new(); |
| 448 | |
| 449 | // Phase 2+4: Filter files by view membership |
| 450 | let file_items: Vec<&OutputItem> = items |
| 451 | .iter() |
| 452 | .filter(|item| { |
| 453 | if item.is_directory { |
| 454 | return false; |
| 455 | } |
| 456 | if !options.matches_prefix(&item.path) { |
| 457 | return false; |
| 458 | } |
| 459 | // View-aware filter: skip files whose introducing change |
| 460 | // is not in the visible change set |
| 461 | if let Some(ref filter) = options.change_filter { |
| 462 | if !item.position.change.is_root() && !filter.contains(&item.position.change) { |
| 463 | return false; |
| 464 | } |
| 465 | } |
| 466 | // Selective materialize: skip files not in the explicit path set |
| 467 | if let Some(ref paths) = only_paths { |
| 468 | if !paths.contains(&item.path) { |
| 469 | return false; |
| 470 | } |
| 471 | } |
| 472 | true |
no test coverage detected