Parse files from a git diff. For each changed file we capture: - The operation type (Added / Modified / Deleted / Renamed / Copied) - The new file content (for adds/modifies) - The old file content (for modifies/deletes) - The exact diff lines that git computed, so Phase 2 can build BranchOps directly from git's diff rather than re-diffing.
(
git_repo: &GitRepository,
diff: &Diff,
tree: &Tree,
parent_tree: Option<&Tree>,
capture_diff_lines: bool,
)
| 3765 | /// - The exact diff lines that git computed, so Phase 2 can build |
| 3766 | /// BranchOps directly from git's diff rather than re-diffing. |
| 3767 | fn parse_diff_files( |
| 3768 | git_repo: &GitRepository, |
| 3769 | diff: &Diff, |
| 3770 | tree: &Tree, |
| 3771 | parent_tree: Option<&Tree>, |
| 3772 | capture_diff_lines: bool, |
| 3773 | ) -> CliResult<Vec<ParsedFile>> { |
| 3774 | use std::collections::HashMap; |
| 3775 | |
| 3776 | // ── Step 1: collect per-file diff lines via diff.foreach ──────────── |
| 3777 | // |
| 3778 | // git2::Diff::foreach gives us each DiffLine with its origin (`+`/`-`/` `), |
| 3779 | // raw bytes, and old/new line numbers — exactly what `git diff` outputs. |
| 3780 | // We key by file path so we can attach them to the ParsedFile below. |
| 3781 | |
| 3782 | // Map from file path → accumulated diff lines for that file. |
| 3783 | let mut lines_by_path: HashMap<String, Vec<GitDiffLine>> = HashMap::new(); |
| 3784 | |
| 3785 | if capture_diff_lines { |
| 3786 | let _ = diff.foreach( |
| 3787 | &mut |_delta, _progress| true, // file_cb (no-op) |
| 3788 | None, // binary_cb |
| 3789 | None, // hunk_cb |
| 3790 | Some(&mut |delta, _hunk, line| { |
| 3791 | let origin = line.origin(); |
| 3792 | // We only keep `+`, `-`, and context (` `) lines. |
| 3793 | if origin != '+' && origin != '-' && origin != ' ' { |
| 3794 | return true; |
| 3795 | } |
| 3796 | let path = delta |
| 3797 | .new_file() |
| 3798 | .path() |
| 3799 | .or_else(|| delta.old_file().path()) |
| 3800 | .map(|p| p.to_string_lossy().to_string()) |
| 3801 | .unwrap_or_default(); |
| 3802 | |
| 3803 | lines_by_path.entry(path).or_default().push(GitDiffLine { |
| 3804 | origin, |
| 3805 | content: line.content().to_vec(), |
| 3806 | old_lineno: line.old_lineno(), |
| 3807 | new_lineno: line.new_lineno(), |
| 3808 | }); |
| 3809 | true |
| 3810 | }), |
| 3811 | ); |
| 3812 | } |
| 3813 | |
| 3814 | // ── Step 2: build ParsedFile entries from the delta list ───────────── |
| 3815 | |
| 3816 | let mut files = Vec::new(); |
| 3817 | |
| 3818 | for delta in diff.deltas() { |
| 3819 | let new_file = delta.new_file(); |
| 3820 | let old_file = delta.old_file(); |
| 3821 | |
| 3822 | // Skip submodules silently (warnings printed during Phase 2) |
| 3823 | if new_file.mode() == git2::FileMode::Commit || old_file.mode() == git2::FileMode::Commit { |
| 3824 | continue; |
no test coverage detected