Add optional pre-commit hook for code formatting #5178
5 issues
High
Pre-commit hook auto-formats files instead of verifying, contradicting stated design - `.githooks/pre-commit:22-24`
Line 22 invokes dotnet format without --verify-no-changes, so the command writes formatting fixes directly to the developer's working tree rather than just verifying. The PR description explicitly states the hook uses 'Check-only mode: Hook verifies formatting but doesn't auto-fix,' but the implementation does the opposite. This is an unintended side effect: a developer running git commit will have their files silently modified on disk, which is surprising behavior and diverges from CI (which uses --verify-no-changes).
Pre-commit hook silently auto-formats staged files, modifying tracked content without consent - `.githooks/pre-commit:22-24`
The hook runs dotnet format without --verify-no-changes, which mutates files in the working tree. Because the hook only checks git diff (unstaged changes) afterward, any formatting fixes applied by dotnet format will modify the working tree but the original (unformatted) staged content is still what gets committed. The user is told to git add -u and recommit, but if the working tree happens to match the index after formatting (e.g., file was already formatted), the hook exits 0 and an unformatted snapshot is committed. This contradicts the PR description which claims 'check-only mode' using --verify-no-changes.
Also found at:
.githooks/pre-commit:6-10
Medium
`./**/*OptionsSetup.cs` exclude pattern silently fails to match nested files - `.githooks/pre-commit:24`
The exclude argument on line 24 uses ./**/*OptionsSetup.cs, which requires bash's globstar option to recurse into subdirectories. The script does not call shopt -s globstar, so ** behaves like a single * and only matches files one directory deep relative to the CWD. As a result, *OptionsSetup.cs files in deeper paths will not be excluded and will be formatted/checked, defeating the intent of the exclusion.
Filenames with spaces or special characters break the --include argument list - `.githooks/pre-commit:12-15`
The loop on lines 12-15 reads staged filenames via git diff --cached --name-only and passes them to dotnet format --include. git diff --name-only does not quote special characters by default unless core.quotePath is configured, and even with proper reading, filenames with spaces will be passed correctly to --include as separate args — but newer git versions or filenames containing quotes/backslashes produce C-style quoted output (e.g., "foo\tbar.cs") which would not match real paths. This can cause the hook to either miss files or fail unexpectedly.
Low
Suppressing dotnet format output hides real errors from developers - `.githooks/pre-commit:24`
Redirecting both stdout and stderr to /dev/null on line 24 means that genuine failures of dotnet format (e.g., missing SDK, project load errors, MSBuild failures) are silently swallowed. With set -e at the top, the script will then abort with no diagnostic, leaving developers unable to understand why their commit was rejected.
4 skills analyzed
| Skill | Findings | Duration | Cost |
|---|---|---|---|
| code-review | 2 | 40.1s | $1.22 |
| find-bugs | 3 | 44.7s | $0.62 |
| gha-security-review | 0 | 24.0s | $0.39 |
| security-review | 0 | 19.8s | $0.56 |
Duration: 2m 9s · Tokens: 265.7k in / 5.3k out · Cost: $2.80 (+merge: $0.00, +consolidate: $0.00, +dedup: $0.01)