Support all-parent merge diffs
ober
37572290becfc2e63e699512607e3d72ddeee142
--- a/README.md +++ b/README.md @@ -32,7 +32,7 @@ jerboa main-binary.ss scan /path/to/repo --format json Supported options are `--config FILE`, `--count N`, `--all`, `--from REV`, `--to REV`, `--commit REV`, `--file PATH`, `--include PATH`, -`--exclude PREFIX`, `--min-lines N`, `--max-files N`, +`--exclude PREFIX`, `--first-parent`, `--all-parents`, `--min-lines N`, `--max-files N`, `--max-added-lines N`, `--max-note-bytes N`, `--threshold HUMAN,AI`, `--threshold HUMAN,UNCERTAIN,AI`, `--no-llm`, `--llm`, `--provider NAME`, @@ -59,6 +59,10 @@ JSON output. `--llm` is accepted as an explicit request, but this release has no active provider adapter; it emits a warning and still reports `llm_used:false` and `network_used:false`. +Merge commits default to first-parent diffs. Use `--all-parents` to inspect +per-parent merge diffs via Git's `-m` mode; merge warnings state which mode was +used. + By default, scans skip `vendor/`, `generated/`, `dist/`, `node_modules/`, and `.git/` paths. Use `--include PATH` or `--file PATH` to inspect one of those paths explicitly. `--include` and `--file` accept repository-relative pathspecs; --- a/main-binary.ss +++ b/main-binary.ss @@ -2,6 +2,22 @@ (import (std misc process)) (def detector-version "0.1.0") +(def current-parent-diff-mode "first-parent") + +(def (use-first-parent-diffs!) + (set! current-parent-diff-mode "first-parent")) + +(def (use-all-parent-diffs!) + (set! current-parent-diff-mode "all-parents")) + +(def (all-parent-diffs?) + (same-public-string? current-parent-diff-mode "all-parents")) + +(def (git-show-diff-args rev extras) + (append (list "show" "--format=") + (if (all-parent-diffs?) (list "-m") (list "--first-parent")) + extras + (list rev))) (def us (integer->char 31)) (def tab (integer->char 9)) @@ -23,7 +39,7 @@ xs))) (def (usage) - (displayln "usage: jerboa main-binary.ss scan [PATH] [--config FILE] [--count N|--all] [--from REV] [--to REV] [--file PATH|--include PATH] [--exclude PREFIX] [--min-lines N] [--threshold HUMAN,AI] [--no-llm|--llm] [--provider NAME] [--max-files N] [--max-added-lines N] [--max-note-bytes N] [--format table|json|jsonl|markdown]") + (displayln "usage: jerboa main-binary.ss scan [PATH] [--config FILE] [--count N|--all] [--from REV] [--to REV] [--file PATH|--include PATH] [--exclude PREFIX] [--first-parent|--all-parents] [--min-lines N] [--threshold HUMAN,AI] [--no-llm|--llm] [--provider NAME] [--max-files N] [--max-added-lines N] [--max-note-bytes N] [--format table|json|jsonl|markdown]") (displayln " jerboa main-binary.ss explain REV [PATH] [--format json|markdown|table]") (displayln " jerboa main-binary.ss stats [PATH] [--count N]") (displayln " jerboa main-binary.ss verify-authorship [PATH] [--count N]")) @@ -252,7 +268,7 @@ [path (safe-ref parts 2 "")]) (list path adds dels))) (def (numstat-lines repo rev file) - (let ([args (append (list "show" "--format=" "--numstat" "--first-parent" rev) + (let ([args (append (git-show-diff-args rev '("--numstat")) (pathspec-args file))]) (filter (lambda (line) (not (blank? line))) (split-lines (git repo args))))) @@ -290,7 +306,7 @@ (def (numstat-paths files) (map car files)) (def (added-lines repo rev file) - (let ([args (append (list "show" "--format=" "--first-parent" "--unified=0" "--no-ext-diff" rev) + (let ([args (append (git-show-diff-args rev '("--unified=0" "--no-ext-diff")) (pathspec-args file))]) (map (lambda (line) (substring line 1 (string-length line))) (filter (lambda (line) @@ -573,7 +589,11 @@ (def (commit-shape-warnings parents) (append (if (root-commit? parents) '("root commit; no parent diff baseline") '()) - (if (merge-commit? parents) '("merge commit; scanner uses first-parent diff") '()))) + (if (merge-commit? parents) + (if (all-parent-diffs?) + '("merge commit; scanner uses all-parent diffs") + '("merge commit; scanner uses first-parent diff")) + '()))) (def (parent-history-warnings shallow? missing-parent?) (append (if shallow? '("repository is shallow; parent history may be unavailable") '()) (if missing-parent? '("parent object unavailable; history may be shallow") '()))) @@ -878,6 +898,14 @@ (options-commit opts) (options-from opts) (options-to opts) (cadr xs) (options-min-lines opts) (options-metadata-only? opts) (options-heuristics-only? opts)) path-set?)] + [(string=? (car xs) "--first-parent") + (begin + (use-first-parent-diffs!) + (loop (cdr xs) opts path-set?))] + [(string=? (car xs) "--all-parents") + (begin + (use-all-parent-diffs!) + (loop (cdr xs) opts path-set?))] [(and (string=? (car xs) "--exclude") (pair? (cdr xs))) (begin (set! current-excludes (cons (cadr xs) current-excludes)) --- a/tests/fixture-smoke.sh +++ b/tests/fixture-smoke.sh @@ -207,6 +207,11 @@ merge_rev=$(git -C "$fixture" rev-parse HEAD) merge_json=$("$root/bin/jerboa-aigit" scan "$fixture" --format json --commit "$merge_rev") printf '%s\n' "$merge_json" | grep -q 'merge commit; scanner uses first-parent diff' +all_parent_json=$("$root/bin/jerboa-aigit" scan "$fixture" --format json --commit "$merge_rev" --all-parents) +printf '%s\n' "$all_parent_json" | grep -q 'merge commit; scanner uses all-parent diffs' +printf '%s\n' "$all_parent_json" | grep -q 'branch/feature.txt' +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' \