| 363 | /// placeholders and the CRDT layer is effectively insert-only. |
| 364 | #[allow(clippy::type_complexity)] |
| 365 | pub fn record_modified_file<W>( |
| 366 | working_copy: &W, |
| 367 | detected: &DetectedFile, |
| 368 | old_content: &[u8], |
| 369 | crdt_old_content: Option<&[u8]>, |
| 370 | options: &RecordingOptions, |
| 371 | existing_trunk_id: Option<TrunkId>, |
| 372 | existing_branches: Option<&[BranchId]>, |
| 373 | ) -> Result<RecordedFile, String> |
| 374 | where |
| 375 | W: WorkingCopyRead, |
| 376 | { |
| 377 | let mut recorded = RecordedFile::new(&detected.path); |
| 378 | recorded.set_kind(DetectionKind::Modified); |
| 379 | |
| 380 | // Count lines in old content for precise insertion point detection |
| 381 | let old_line_count = old_content.iter().filter(|&&b| b == b'\n').count(); |
| 382 | recorded.set_old_line_count(old_line_count); |
| 383 | |
| 384 | // Copy inode and position if available |
| 385 | if let Some(inode) = detected.inode { |
| 386 | recorded.set_inode(inode); |
| 387 | } |
| 388 | if let Some(position) = detected.position { |
| 389 | recorded.set_position(position); |
| 390 | } |
| 391 | |
| 392 | // Read new content from working copy |
| 393 | let mut new_content = Vec::new(); |
| 394 | if let Err(e) = working_copy.read_file(&detected.path, &mut new_content) { |
| 395 | return Err(format!("Failed to read file {}: {}", detected.path, e)); |
| 396 | } |
| 397 | |
| 398 | // Check size limits |
| 399 | if options.exceeds_max_size(new_content.len()) { |
| 400 | return Err(format!( |
| 401 | "File {} exceeds maximum size ({} bytes)", |
| 402 | detected.path, |
| 403 | new_content.len() |
| 404 | )); |
| 405 | } |
| 406 | |
| 407 | // Detect encoding |
| 408 | let encoding = detect_encoding(&new_content); |
| 409 | recorded.set_encoding(encoding); |
| 410 | |
| 411 | // Skip binary files if configured |
| 412 | if encoding == Encoding::Binary && options.get_skip_binary() { |
| 413 | return Err(format!("Skipping binary file {}", detected.path)); |
| 414 | } |
| 415 | |
| 416 | // Fast path: machine-generated files (lockfiles, bundled output) skip |
| 417 | // both the expensive diff computation AND CRDT tokenization. We treat |
| 418 | // them as a whole-file replace — one vertex in, one vertex out. |
| 419 | if super::globalize::should_use_opaque_generated_vertices(&detected.path) { |
| 420 | let mut replace_hunk = BuiltHunk::new_replace_with_lines( |
| 421 | Local::new(&detected.path, 1), |
| 422 | Some(encoding), |