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>>,
)
| 199 | /// Falls back to sequential processing for files that fail in the |
| 200 | /// parallel path. |
| 201 | pub fn materialize_parallel( |
| 202 | &self, |
| 203 | only_paths: Option<std::collections::HashSet<String>>, |
| 204 | ) -> Result<MaterializeResult, RepositoryError> { |
| 205 | use atomic_core::output::repo::{ |
| 206 | collect_children, FileOutputOptions, MaterializeOptions, OutputItem, |
| 207 | }; |
| 208 | use atomic_core::output::RetrieveOptions; |
| 209 | use rayon::prelude::*; |
| 210 | use std::collections::HashSet as StdHashSet; |
| 211 | |
| 212 | let txn = self |
| 213 | .pristine |
| 214 | .read_txn() |
| 215 | .map_err(|e| RepositoryError::Database(e.to_string()))?; |
| 216 | |
| 217 | let view = txn |
| 218 | .get_view(&self.current_view) |
| 219 | .map_err(|e| RepositoryError::Database(e.to_string()))? |
| 220 | .ok_or_else(|| RepositoryError::ViewNotFound { |
| 221 | name: self.current_view.clone(), |
| 222 | })?; |
| 223 | |
| 224 | let change_filter = collect_visible_change_ids(&txn, &view)?; |
| 225 | let change_filter_arc = Arc::new(change_filter); |
| 226 | |
| 227 | let options = MaterializeOptions::new().with_change_filter_arc(change_filter_arc.clone()); |
| 228 | |
| 229 | // Phase 1: Collect all items from the tree |
| 230 | let items = collect_children(&txn, Inode::ROOT, "", &options) |
| 231 | .map_err(|e| RepositoryError::Database(e.to_string()))?; |
| 232 | |
| 233 | let _file_options = FileOutputOptions::new(); |
| 234 | |
| 235 | // Phase 2+4: Filter files by view membership |
| 236 | let file_items: Vec<&OutputItem> = items |
| 237 | .iter() |
| 238 | .filter(|item| { |
| 239 | if item.is_directory { |
| 240 | return false; |
| 241 | } |
| 242 | if !options.matches_prefix(&item.path) { |
| 243 | return false; |
| 244 | } |
| 245 | // View-aware filter: skip files whose introducing change |
| 246 | // is not in the visible change set |
| 247 | if let Some(ref filter) = options.change_filter { |
| 248 | if !item.position.change.is_root() && !filter.contains(&item.position.change) { |
| 249 | return false; |
| 250 | } |
| 251 | } |
| 252 | // Selective materialize: skip files not in the explicit path set |
| 253 | if let Some(ref paths) = only_paths { |
| 254 | if !paths.contains(&item.path) { |
| 255 | return false; |
| 256 | } |
| 257 | } |
| 258 | true |
no test coverage detected