Skip to content

review diff_parser

aakash-anko edited this page Jul 10, 2026 · 3 revisions

review / diff_parser.py

Runs git diff and parses raw unified diff text into structured DiffFile objects using the unidiff library.


Key Concepts

Term Definition Example
diff The set of changes between two versions of code, showing added (+) and removed (-) lines. - old_line\n+ new_line shows old_line was replaced with new_line.
hunk A contiguous block of changes within a diff. One diff can contain multiple hunks (changes in different parts of a file). A diff might have hunk 1 (lines 10-15 changed) and hunk 2 (lines 80-85 changed).

get_diff — line 8

Runs git diff as a subprocess and returns the raw unified diff text as a string. By default, returns ALL changes: staged, unstaged, AND new untracked files.

Parameters

Param Type Default Purpose
staged bool False If True, diffs ONLY staged changes (narrow mode — no unstaged or untracked)
target_branch str | None None Diff working tree against this branch (two-dot: committed + staged + unstaged + untracked)
commit str | None None If set, shows diff for that specific commit (SHA or ref). No untracked files.
since_commit str | None None Diff from this commit to working tree + untracked.
repo_path str | None None Working directory for the git command

Priority: commit > since_commit > target_branch > staged > default (git diff HEAD).

Behavior Table

commit           → git diff <sha>~1 <sha>                (historical, no untracked)
since_commit     → git diff <sha>               + untracked
target_branch    → git diff <branch>             + untracked   (two-dot diff)
staged=True      → git diff --staged                          (narrow, no untracked)
default          → git diff HEAD                 + untracked
default (no HEAD)→ git diff --cached             + untracked   (empty repo)

Example 1: Default — all local changes

Input: repo_path="/home/user/myapp" (no other args)

Runs git diff HEAD --unified=5 → captures staged + unstaged changes. Then appends synthetic diffs for untracked files via _synthetic_untracked_diff(). Returns combined diff text.

Example 2: Diff against a branch

Input: target_branch="main", repo_path=None

Runs git diff main --unified=5 → two-dot diff showing everything different from main's tip (committed + staged + unstaged). Then appends synthetic diffs for untracked files.

Example 3: Review a specific commit

Input: commit="abc1234", staged=True, target_branch="main", repo_path="/project"

commit takes highest priority — staged and target_branch are ignored. Runs git diff abc1234~1 abc1234 → shows what that commit changed. No untracked files appended (historical snapshot).

_synthetic_untracked_diff(repo_path)

Generates unified-diff text for untracked files not yet staged. Runs git ls-files --others --exclude-standard and builds a synthetic diff for each file. Skips: binary files (null byte in first 8KB), files > 512KB, symlinks, non-UTF-8 files.


get_parsed_diff — line 25

Parses a raw unified diff string into a list of DiffFile objects using the unidiff.PatchSet parser.

Parameters

Param Type Purpose
diff_text str Raw unified diff text from git diff

Example

Input:

diff_text = """diff --git a/math.py b/math.py
--- a/math.py
+++ b/math.py
@@ -10,3 +10,3 @@
 def add(a, b):
-    return a - b
+    return a + b
"""

Walkthrough:

Line 28–29: diff_text.strip() is non-empty → proceed

Line 31: patch = PatchSet(diff_text) → parses into a PatchSet with 1 PatchedFile

Line 32: diff_files = []

Loop — patched_file = first (and only) PatchedFile for math.py:

Line 35: hunks = []

Loop — hunk = first hunk (the @@ block):

Line 37: lines = []

Loop — line = first line (def add(a, b):):

Line 38–40: line.is_addedFalse, line.is_removedFalse Line 44–45: → change_type = "context", line_no = 10 (target_line_no) Line 47–50: appends ChangedLine(line_number=10, content="def add(a, b):\n", change_type="context")

Loop — line = second line (- return a - b):

Line 41–43: line.is_removedTruechange_type = "removed", line_no = 11 (source_line_no) Line 47–50: appends ChangedLine(line_number=11, content=" return a - b\n", change_type="removed")

Loop — line = third line (+ return a + b):

Line 38–40: line.is_addedTruechange_type = "added", line_no = 11 (target_line_no) Line 47–50: appends ChangedLine(line_number=11, content=" return a + b\n", change_type="added")

Line 52–55: appends DiffHunk(start_line=10, end_line=13, lines=[...3 ChangedLines...])

Line 57–65: appends DiffFile(...):

DiffFile(
    file_path="math.py",
    language=detect_language(Path("math.py")),   →  "python"
    hunks=[DiffHunk(start_line=10, end_line=13, lines=[...])],
    is_new_file=False,
    is_deleted=False,
    added_lines=1,
    removed_lines=1,
)

Return: [DiffFile(file_path="math.py", language="python", added_lines=1, removed_lines=1, ...)]

Clone this wiki locally