Skip to content

Re-stage formatted files in the pre-commit hook under POSIX sh - #537

Merged
thomasmny merged 1 commit into
thomasmny:masterfrom
hbrombeer:fix/pre-commit-restage-posix
Sep 30, 2026
Merged

thomasmny merged 1 commit into
thomasmny:masterfrom
hbrombeer:fix/pre-commit-restage-posix

Conversation

@hbrombeer

Copy link
Copy Markdown
Contributor

The pre-commit hook runs as #!/bin/sh, but its re-stage loop uses read -d '', which is a bash extension. Where /bin/sh is dash (Debian, Ubuntu, WSL) the loop fails on its first read:

.githooks/pre-commit: 23: read: Illegal option -d

The hook still exits 0, so the commit goes through with the unformatted content, while the formatted version stays behind as an unstaged change. In addition, staged=$(git diff --cached --name-only -z) loses the NUL separators, because command substitution drops NUL bytes.

This change pipes the staged list straight into xargs -0 sh -c 'for f do …; done' sh. It keeps odd paths intact and works in any POSIX shell. The early exit uses git diff --cached --quiet, and --diff-filter=ACMR skips deleted paths. Behaviour is otherwise unchanged: only files that were already staged and that Spotless modified are re-staged.

Tested under dash with a stub gradlew that appends to *.java:

  • before: read: Illegal option -d, commit contains the unformatted file
  • after: the staged file is committed formatted, an unstaged file is not swept in, a path with a space is handled, and an empty staged list exits 0

Unrelated, but hit at the same time: palantirJavaFormat("2.92.0") fails on JDK 25+ with NoSuchFieldError: … JCCompilationUnit does not have member field 'EndPosTable endPositions', so the hook only works when Gradle runs on JDK 21. 2.100.0 works on JDK 25 but reformats a few files, so I left that for you to decide.

The hook runs as /bin/sh, and `read -d` is a bash extension: under dash
(Debian and Ubuntu's /bin/sh) the loop failed on its first read, so no
file Spotless reformatted was ever re-staged and commits kept the
unformatted content. Command substitution also dropped the NUL bytes the
-z list relied on.

Pipe the staged list straight into `xargs -0` instead, which keeps odd
paths intact and works in any POSIX shell.
@thomasmny
thomasmny merged commit b090f2d into thomasmny:master Sep 30, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants