Warn on rename and copy diffs
ober
4bdc73e7a2e5ce414fb6e9f7c641e61454b60324
--- a/README.md +++ b/README.md @@ -86,8 +86,8 @@ uses median/MAD-style addition-count deviation across the selected scan window. Warnings also call out root commits with no parent baseline, merge commits where the scanner intentionally uses the first-parent diff, missing parent objects in -shallow history, and binary file changes that are skipped by text-line -heuristics. +shallow history, rename/copy changes, and binary file changes that are skipped +by text-line heuristics. ## What It Reads --- a/main-binary.ss +++ b/main-binary.ss @@ -292,6 +292,29 @@ (def (binary-file-count repo rev file) (count-where binary-numstat-line? (selected-numstat-lines (numstat-lines repo rev file) file))) +(def (status-lines repo rev file) + (let ([args (append (git-show-diff-args rev '("--name-status" "--find-renames" "--find-copies")) + (pathspec-args file))]) + (filter (lambda (line) (not (blank? line))) + (split-lines (git repo args))))) + +(def (status-line-path line) + (let ([parts (split-tabs line)]) + (if (>= (length parts) 3) + (safe-ref parts 2 "") + (safe-ref parts 1 "")))) + +(def (selected-status-lines lines explicit-file) + (if explicit-file + lines + (filter (lambda (line) (not (excluded-path? (status-line-path line)))) lines))) + +(def (rename-copy-status-line? line) + (let ([status (safe-ref (split-tabs line) 0 "")]) + (or (string-prefix? "R" status) (string-prefix? "C" status)))) + +(def (rename-copy-change-count repo rev file) + (count-where rename-copy-status-line? (selected-status-lines (status-lines repo rev file) file))) (def (changed-files repo rev file) (let* ([lines (numstat-lines repo rev file)] @@ -634,11 +657,16 @@ (if (> binary-count 0) (list (str "binary file changes skipped: " binary-count)) '())) +(def (rename-copy-warnings rename-copy-count) + (if (> rename-copy-count 0) + (list (str "rename/copy changes detected: " rename-copy-count)) + '())) -(def (warnings files lines note note-obj metadata-only? heuristics-only? min-lines file-count line-count note-count parents shallow? binary-count missing-parent?) +(def (warnings files lines note note-obj metadata-only? heuristics-only? min-lines file-count line-count note-count parents shallow? binary-count rename-copy-count missing-parent?) (append (commit-shape-warnings parents) (parent-history-warnings shallow? missing-parent?) (binary-change-warnings binary-count) + (rename-copy-warnings rename-copy-count) (llm-warnings) (if (null? files) '("no changed text files found or commit is unavailable") '()) (if (null? lines) '("no added UTF-8 patch lines available") '()) @@ -661,6 +689,7 @@ [time (parse-int (safe-ref fields 4 "0") 0)] [subject (safe-ref fields 5 "")] [body (commit-message repo rev)] [file-count (changed-file-count repo rev file)] [binary-count (binary-file-count repo rev file)] + [rename-copy-count (rename-copy-change-count repo rev file)] [files (changed-files repo rev file)] [paths (numstat-paths files)] [adds (numstat-adds files)] [dels (numstat-dels files)] [line-count (added-line-count-for-paths repo rev paths file)] @@ -683,7 +712,7 @@ [heuristics-only? (verdict score '() "")] [else (verdict score metadata note)])]) (list (make-finding id parent author-name author-email time subject paths adds dels (length lines) note attribution metadata signals score v - (warnings files lines note note-obj metadata-only? heuristics-only? min-lines file-count line-count note-count parents shallow? binary-count missing-parent?)) + (warnings files lines note note-obj metadata-only? heuristics-only? min-lines file-count line-count note-count parents shallow? binary-count rename-copy-count missing-parent?)) (cadr sim-pair)))) (def (scan-repo repo revs file min-lines metadata-only? heuristics-only?) --- a/tests/fixture-smoke.sh +++ b/tests/fixture-smoke.sh @@ -206,22 +206,29 @@ printf '%s\n' "$insufficient" | grep -q '"verdict":"insufficient-evidence"' printf '%s\n' "$insufficient" | grep -q '"signals":\[\]' printf '%s\n' "$insufficient" | grep -q 'no changed text files found or commit is unavailable' +git -C "$fixture" mv README.md README-renamed.md +GIT_AUTHOR_DATE='2026-07-29T09:03:00-06:00' \ +GIT_COMMITTER_DATE='2026-07-29T09:03:00-06:00' \ + git -C "$fixture" commit -q -m 'rename readme' +rename_json=$("$root/bin/jerboa-aigit" scan "$fixture" --format json --count 1) +printf '%s\n' "$rename_json" | grep -q 'rename/copy changes detected: 1' + base_branch=$(git -C "$fixture" branch --show-current) git -C "$fixture" checkout -q -b feature-branch mkdir -p "$fixture/branch" printf 'branch work\n' > "$fixture/branch/feature.txt" git -C "$fixture" add branch/feature.txt -GIT_AUTHOR_DATE='2026-07-29T09:03:00-06:00' \ -GIT_COMMITTER_DATE='2026-07-29T09:03:00-06:00' \ +GIT_AUTHOR_DATE='2026-07-29T09:04:00-06:00' \ +GIT_COMMITTER_DATE='2026-07-29T09:04:00-06:00' \ git -C "$fixture" commit -q -m 'feature branch work' git -C "$fixture" checkout -q "$base_branch" printf 'main work\n' > "$fixture/src/human.txt" git -C "$fixture" add src/human.txt -GIT_AUTHOR_DATE='2026-07-29T09:04:00-06:00' \ -GIT_COMMITTER_DATE='2026-07-29T09:04:00-06:00' \ - git -C "$fixture" commit -q -m 'main branch work' GIT_AUTHOR_DATE='2026-07-29T09:05:00-06:00' \ GIT_COMMITTER_DATE='2026-07-29T09:05:00-06:00' \ + git -C "$fixture" commit -q -m 'main branch work' +GIT_AUTHOR_DATE='2026-07-29T09:06:00-06:00' \ +GIT_COMMITTER_DATE='2026-07-29T09:06:00-06:00' \ git -C "$fixture" merge -q --no-ff feature-branch -m 'merge feature branch' merge_rev=$(git -C "$fixture" rev-parse HEAD) merge_json=$("$root/bin/jerboa-aigit" scan "$fixture" --format json --commit "$merge_rev") @@ -234,13 +241,13 @@ printf '%s\n' "$all_parent_json" | grep -q 'src/human.txt' printf '*.bin binary\n' > "$fixture/.gitattributes" git -C "$fixture" add .gitattributes -GIT_AUTHOR_DATE='2026-07-29T09:06:00-06:00' \ -GIT_COMMITTER_DATE='2026-07-29T09:06:00-06:00' \ +GIT_AUTHOR_DATE='2026-07-29T09:07:00-06:00' \ +GIT_COMMITTER_DATE='2026-07-29T09:07:00-06:00' \ git -C "$fixture" commit -q -m 'mark binary assets' printf '\000\001binary\002\n' > "$fixture/model.bin" git -C "$fixture" add model.bin -GIT_AUTHOR_DATE='2026-07-29T09:07:00-06:00' \ -GIT_COMMITTER_DATE='2026-07-29T09:07:00-06:00' \ +GIT_AUTHOR_DATE='2026-07-29T09:08:00-06:00' \ +GIT_COMMITTER_DATE='2026-07-29T09:08:00-06:00' \ git -C "$fixture" commit -q -m 'add binary model asset' binary_rev=$(git -C "$fixture" rev-parse HEAD) binary_json=$("$root/bin/jerboa-aigit" scan "$fixture" --format json --commit "$binary_rev")