Assemble a change from recorded files. This is the main entry point for creating a Change from recording results. It globalizes all hunks, collects dependencies, and creates the final Change structure. # Arguments `txn` - Transaction for graph lookups `files` - The recorded files to assemble `header` - The change header `options` - Assembly options # Returns The assembled change, or an error
(
txn: &T,
files: &[RecordedFile],
header: ChangeHeader,
options: &AssemblyOptions,
)
| 341 | /// let change = assemble_change(&txn, &recorded_files, header, &AssemblyOptions::default())?; |
| 342 | /// ``` |
| 343 | pub fn assemble_change<T>( |
| 344 | txn: &T, |
| 345 | files: &[RecordedFile], |
| 346 | header: ChangeHeader, |
| 347 | options: &AssemblyOptions, |
| 348 | ) -> types::AssemblyResult<AssemblyResult_> |
| 349 | where |
| 350 | T: GraphTxnT + TreeTxnT + crate::pristine::InodeGraphOps, |
| 351 | { |
| 352 | // Validate input |
| 353 | if files.is_empty() { |
| 354 | return Err(types::AssemblyError::NoFiles); |
| 355 | } |
| 356 | |
| 357 | log::debug!("assemble_change: {} files", files.len(),); |
| 358 | |
| 359 | // Create globalization context |
| 360 | let mut glob_ctx = GlobalizeContext::new(txn); |
| 361 | let mut ctx = AssemblyContext::new(header); |
| 362 | let mut stats = AssemblyStats::new(); |
| 363 | let mut globalized_files = Vec::new(); |
| 364 | let mut globalize_errors = Vec::new(); |
| 365 | |
| 366 | // Process each file |
| 367 | let total_files = files.len(); |
| 368 | let assembly_start = Instant::now(); |
| 369 | |
| 370 | for (file_idx, file) in files.iter().enumerate() { |
| 371 | stats.record_file(); |
| 372 | |
| 373 | // Skip empty files if configured, but never skip directories |
| 374 | // (directories generate hunks during globalization, not during recording) |
| 375 | if file.is_empty() |
| 376 | && !options.get_include_empty_files() |
| 377 | && !file.is_directory() |
| 378 | && !file.is_deleted_directory() |
| 379 | { |
| 380 | log::debug!( |
| 381 | "assemble_change: file {}/{} '{}' skipped (no hunks)", |
| 382 | file_idx + 1, |
| 383 | total_files, |
| 384 | file.path(), |
| 385 | ); |
| 386 | stats.record_skip(); |
| 387 | continue; |
| 388 | } |
| 389 | |
| 390 | // Skip newly-added files with empty content (e.g., 0-byte files like |
| 391 | // .nojekyll, .gitkeep). These have hunks from the recording phase but |
| 392 | // no actual content bytes, so globalization will produce nothing — |
| 393 | // avoid the expensive globalize_recorded_file call entirely. |
| 394 | if file.inode().is_none() |
| 395 | && file.content().is_empty() |
| 396 | && !file.is_directory() |
| 397 | && !file.is_deleted_directory() |
| 398 | && !options.get_include_empty_files() |
| 399 | { |
| 400 | log::debug!( |
no test coverage detected