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>>,
)
| 539 | /// Falls back to sequential processing for files that fail in the |
| 540 | /// parallel path. |
| 541 | pub fn materialize_parallel( |
| 542 | &self, |
| 543 | only_paths: Option<std::collections::HashSet<String>>, |
| 544 | ) -> Result<MaterializeResult, RepositoryError> { |
| 545 | use atomic_core::output::repo::{ |
| 546 | collect_children, FileOutputOptions, MaterializeOptions, OutputItem, |
| 547 | }; |
| 548 | use atomic_core::output::RetrieveOptions; |
| 549 | use rayon::prelude::*; |
| 550 | use std::collections::HashSet as StdHashSet; |
| 551 | |
| 552 | let txn = self |
| 553 | .pristine |
| 554 | .read_txn() |
| 555 | .map_err(|e| RepositoryError::Database(e.to_string()))?; |
| 556 | |
| 557 | let view = txn |
| 558 | .get_view(&self.current_view) |
| 559 | .map_err(|e| RepositoryError::Database(e.to_string()))? |
| 560 | .ok_or_else(|| RepositoryError::ViewNotFound { |
| 561 | name: self.current_view.clone(), |
| 562 | })?; |
| 563 | |
| 564 | let change_filter = collect_visible_change_ids(&txn, &view)?; |
| 565 | let change_filter_arc = Arc::new(change_filter); |
| 566 | |
| 567 | let options = MaterializeOptions::new().with_change_filter_arc(change_filter_arc.clone()); |
| 568 | |
| 569 | // Phase 1: Collect all items from the tree |
| 570 | let items = collect_children(&txn, Inode::ROOT, "", &options) |
| 571 | .map_err(|e| RepositoryError::Database(e.to_string()))?; |
| 572 | |
| 573 | let _file_options = FileOutputOptions::new(); |
| 574 | |
| 575 | // Phase 2+4: Filter files by view membership |
| 576 | let file_items: Vec<&OutputItem> = items |
| 577 | .iter() |
| 578 | .filter(|item| { |
| 579 | if item.is_directory { |
| 580 | return false; |
| 581 | } |
| 582 | if !options.matches_prefix(&item.path) { |
| 583 | return false; |
| 584 | } |
| 585 | // View-aware filter: skip files whose introducing change |
| 586 | // is not in the visible change set |
| 587 | if let Some(ref filter) = options.change_filter { |
| 588 | if !item.position.change.is_root() && !filter.contains(&item.position.change) { |
| 589 | return false; |
| 590 | } |
| 591 | } |
| 592 | // Selective materialize: skip files not in the explicit path set |
| 593 | if let Some(ref paths) = only_paths { |
| 594 | if !paths.contains(&item.path) { |
| 595 | return false; |
| 596 | } |
| 597 | } |
| 598 | true |
no test coverage detected