fix(workspace-changes): count diff body lines starting with "-- "/"++ " (#4303)

`_count_diff_lines` skipped every line starting with "+++ "/"--- " to drop
difflib's two file headers. But a deleted hunk-body line whose content
begins with "-- " (e.g. a SQL comment `-- get users`) becomes the diff
line `--- get users`, and an added `++ ...` line becomes `+++ ...`, so
those were dropped too — undercounting the user-visible +N/-M change
summary (WorkspaceChangeSummary, per-file WorkspaceFileChange, and the
run-event string in recorder.py).

`difflib.unified_diff` always emits the two headers first, so skip them
by position (`lines[2:]`) instead. Hunk `@@` lines start with `@` and are
still ignored. Numeric/plain diffs are unaffected.

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Andrew Chen 2026-07-21 17:48:04 +08:00 committed by GitHub
parent fa496c0c8d
commit f113f10f36
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 33 additions and 6 deletions

View File

@ -198,12 +198,12 @@ def _snapshot_text(file: FileSnapshot | None) -> str | None:
def _count_diff_lines(lines: list[str]) -> tuple[int, int]:
additions = 0
deletions = 0
for line in lines:
# Unified-diff file headers are "+++ " / "--- " with a trailing space;
# a bare "+++"/"---" prefix would also swallow real content lines whose
# text begins with those sequences (e.g. an added line "+++foo").
if line.startswith("+++ ") or line.startswith("--- "):
continue
# difflib.unified_diff always emits the two file headers ("--- a/…" then
# "+++ b/…") first; skip them by position. A prefix test can't tell them
# apart from a hunk-body line whose content starts with "-- "/"++ " (e.g. a
# deleted SQL comment "-- get users" becomes the diff line "--- get users"),
# which would then be dropped from the count.
for line in lines[2:]:
if line.startswith("+"):
additions += 1
elif line.startswith("-"):

View File

@ -182,6 +182,33 @@ def test_count_diff_lines_ignores_only_real_headers():
assert deletions == 2
def test_count_diff_lines_counts_content_starting_with_dashes_or_pluses():
"""Hunk-body lines whose content starts with '-- '/'++ ' must be counted.
difflib prefixes a deleted line "-- get users" to "--- get users"; the old
prefix skip mistook that for a file header and dropped it, undercounting
deletions in the user-visible +N/-M summary.
"""
import difflib
from deerflow.workspace_changes.diff import _count_diff_lines
lines = list(
difflib.unified_diff(
["SELECT 1", "-- get users"],
["SELECT 1", "SELECT 2"],
fromfile="a/x.sql",
tofile="b/x.sql",
lineterm="",
)
)
additions, deletions = _count_diff_lines(lines)
assert additions == 1
assert deletions == 1
def test_scan_workspace_roots_skips_excluded_directories(tmp_path):
roots = _roots(tmp_path)
workspace = roots[0].host_path