Add optional pre-commit hook for code formatting #5178
3 issues
find-bugs: Found 3 issues (1 high, 1 medium, 1 low)
High
Pre-commit hook silently modifies staged files via `dotnet format` - `.githooks/pre-commit:24-26`
dotnet format (without --verify-no-changes) rewrites files on disk. Because git stash push --keep-index leaves the staged content in the working tree, the format command modifies those staged files. The hook then detects the diff, aborts the commit, and the trap pops the stash — but the formatter's modifications to the working tree are NOT reverted. The developer's working copy now contains formatter changes they never asked for, mixed with their original edits, and on the next git add -u/commit they will commit formatter output silently. The PR description claims the hook uses --verify-no-changes (check-only), but the actual script does not.
Also found at:
.githooks/pre-commit:25-26
Medium
Stash detection by message can match unrelated stashes and miss empty stashes - `.githooks/pre-commit:10-16`
git stash push --keep-index creates no stash entry when there are no unstaged changes (and exits 0 due to || true). The later git stash list | grep -q "$STASH_NAME" correctly skips popping in that case, but the pattern match is a substring search against git stash list output, which could be matched by an unrelated user stash whose message happens to contain pre-commit-<timestamp> (unlikely but possible). More importantly, on a stash-conflict pop failure, the script tells the user to run git stash pop, but does not identify which stash to pop — the user may have multiple stashes and pop the wrong one.
Low
setup-hooks.sh does not ensure execute permission on hook script - `scripts/setup-hooks.sh:7`
The setup script configures core.hooksPath but does not ensure that .githooks/pre-commit is executable. If the file is checked out without the executable bit (e.g., on systems where git's filemode is not preserved or the file was added without +x), the pre-commit hook will silently fail to run, defeating the purpose of the hook. Adding chmod +x .githooks/pre-commit in the setup script guarantees the hook runs as intended.
Duration: 38.8s · Tokens: 50.1k in / 2.9k out · Cost: $0.43 (+merge: $0.00)
Annotations
Check failure on line 26 in .githooks/pre-commit
sentry-warden / warden: find-bugs
Pre-commit hook silently modifies staged files via `dotnet format`
`dotnet format` (without `--verify-no-changes`) rewrites files on disk. Because `git stash push --keep-index` leaves the staged content in the working tree, the format command modifies those staged files. The hook then detects the diff, aborts the commit, and the trap pops the stash — but the formatter's modifications to the working tree are NOT reverted. The developer's working copy now contains formatter changes they never asked for, mixed with their original edits, and on the next `git add -u`/commit they will commit formatter output silently. The PR description claims the hook uses `--verify-no-changes` (check-only), but the actual script does not.
Check failure on line 26 in .githooks/pre-commit
sentry-warden / warden: find-bugs
[96L-H56] Pre-commit hook silently modifies staged files via `dotnet format` (additional location)
`dotnet format` (without `--verify-no-changes`) rewrites files on disk. Because `git stash push --keep-index` leaves the staged content in the working tree, the format command modifies those staged files. The hook then detects the diff, aborts the commit, and the trap pops the stash — but the formatter's modifications to the working tree are NOT reverted. The developer's working copy now contains formatter changes they never asked for, mixed with their original edits, and on the next `git add -u`/commit they will commit formatter output silently. The PR description claims the hook uses `--verify-no-changes` (check-only), but the actual script does not.
Check warning on line 16 in .githooks/pre-commit
sentry-warden / warden: find-bugs
Stash detection by message can match unrelated stashes and miss empty stashes
`git stash push --keep-index` creates no stash entry when there are no unstaged changes (and exits 0 due to `|| true`). The later `git stash list | grep -q "$STASH_NAME"` correctly skips popping in that case, but the pattern match is a substring search against `git stash list` output, which could be matched by an unrelated user stash whose message happens to contain `pre-commit-<timestamp>` (unlikely but possible). More importantly, on a stash-conflict pop failure, the script tells the user to run `git stash pop`, but does not identify which stash to pop — the user may have multiple stashes and pop the wrong one.