Show diff using the semantic layer (FileOps). This is the preferred path - it displays line-level and token-level changes directly from the stored CRDT operations, without recomputing. Hunks are anchored at their true file offsets (derived from the line numbers stored on each operation at record time) and padded with context lines from the file's recorded before-content, so downstream renderers
(
&self,
repo: &Repository,
change: &Change,
change_hash: &Hash,
config: &DiffOutputConfig,
)
| 335 | /// context lines from the file's recorded before-content, so downstream |
| 336 | /// renderers see standard unified-diff hunk headers. |
| 337 | fn show_change_diff_from_file_ops( |
| 338 | &self, |
| 339 | repo: &Repository, |
| 340 | change: &Change, |
| 341 | change_hash: &Hash, |
| 342 | config: &DiffOutputConfig, |
| 343 | ) -> CliResult<()> { |
| 344 | if let Some((file_diffs, stats)) = Self::build_git_import_file_diffs(change) { |
| 345 | let (file_diffs, stats) = self.filter_file_diffs(file_diffs, stats); |
| 346 | if file_diffs.is_empty() { |
| 347 | self.print_no_changes(); |
| 348 | return Ok(()); |
| 349 | } |
| 350 | |
| 351 | if config.format == DiffFormat::Unified { |
| 352 | self.print_change_header(change, change_hash, config); |
| 353 | } |
| 354 | |
| 355 | return match config.format { |
| 356 | DiffFormat::Unified => self.print_unified(&file_diffs, config), |
| 357 | DiffFormat::Stat => self.print_stat(&stats, config), |
| 358 | DiffFormat::NameOnly => self.print_name_only(&file_diffs), |
| 359 | DiffFormat::NameStatus => self.print_name_status(&file_diffs, config), |
| 360 | }; |
| 361 | } |
| 362 | |
| 363 | let file_ops = change.file_ops(); |
| 364 | |
| 365 | if file_ops.is_empty() { |
| 366 | self.print_no_changes(); |
| 367 | return Ok(()); |
| 368 | } |
| 369 | |
| 370 | let mut file_diffs = Vec::new(); |
| 371 | let mut stats = DiffStats::new(); |
| 372 | |
| 373 | for ops in file_ops { |
| 374 | let file_path = ops.path(); |
| 375 | |
| 376 | // Honor the positional file filter (`diff --change <hash> <file>`) |
| 377 | if !self.file_matches_filter(file_path) { |
| 378 | continue; |
| 379 | } |
| 380 | |
| 381 | let trunk_op = ops.trunk_op(); |
| 382 | let line_ops = ops.line_ops(); |
| 383 | |
| 384 | // Determine file change status from trunk operation |
| 385 | let change_status = match trunk_op { |
| 386 | Some(TrunkOp::Create { .. }) => FileChangeStatus::Added, |
| 387 | Some(TrunkOp::Delete { .. }) => FileChangeStatus::Deleted, |
| 388 | Some(TrunkOp::Move { .. }) => FileChangeStatus::Renamed, |
| 389 | Some(TrunkOp::Undelete { .. }) => FileChangeStatus::Modified, |
| 390 | None => FileChangeStatus::Modified, |
| 391 | }; |
| 392 | |
| 393 | let mut file_diff = match change_status { |
| 394 | FileChangeStatus::Added => FileDiff::added(file_path), |
no test coverage detected