search you tried in the issue tracker
sha256, SHA-256 push, sha-256 delete, Z40, object-format sha256, "Invalid revision range", "bad object", zero SHA, pre-push delete, deletion push
The closest related hit is #3434 (closed), but that is about fetching sha1-format hook repos into a sha256 local repo — a different codepath.
describe your issue
pre_commit/commands/hook_impl.py hardcodes the SHA-1 zero OID:
It is compared against local_sha / remote_sha to detect deletion-only pushes and new branches:
if local_sha == Z40:
continue
elif remote_sha != Z40 and _rev_exists(remote_sha):
...
In a repository initialized with --object-format=sha256, git's pre-push hook protocol emits 64-character zero OIDs rather than 40. The equality never matches, the deletion-only short-circuit never fires, and pre-commit falls through into git diff --name-only ... <old>..<64-zeros> (and then <old>...<64-zeros>). Both error out:
fatal: Invalid revision range 3dac4c61...6dce8d..000000...(×64)
fatal: Invalid symmetric difference expression 3dac4c61...6dce8d...000000...(×64)
pre-commit surfaces these as An unexpected error has occurred: CalledProcessError and exits non-zero, so git push origin --delete <branch> is refused on every sha256 repo. Branch deletion has to go through the forge's web UI or API to succeed.
Minimal reproducer:
git init --object-format=sha256 repro
cd repro
git config user.email t@e.com && git config user.name T
git commit --allow-empty -m init
cat > .pre-commit-config.yaml <<'EOF'
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v5.0.0
hooks:
- id: trailing-whitespace
EOF
git add .pre-commit-config.yaml && git commit -m "pc"
pre-commit install --hook-type pre-push
git checkout -b doomed
git commit --allow-empty -m x
SHA=$(git rev-parse HEAD)
ZEROS=$(printf '0%.0s' $(seq 1 64))
# Feed git's pre-push stdin format directly — simulates `git push origin --delete doomed`
printf '(delete) %s refs/heads/doomed %s\n' "$ZEROS" "$SHA" | .git/hooks/pre-push origin .
# exit code 3, error above
Suggested fix: accept an all-zero OID of either supported length (40 or 64), or (more general) read the repo's zero OID at runtime. A one-liner that covers both currently-supported object formats:
def _is_zero_oid(oid: str) -> bool:
return len(oid) in (40, 64) and set(oid) == {'0'}
Callers become if _is_zero_oid(local_sha): continue and elif not _is_zero_oid(remote_sha) and _rev_exists(remote_sha):. A strictly hash-agnostic alternative reads git rev-parse --show-object-format once per hook invocation and computes the zero string from it.
Happy to open a PR if helpful.
pre-commit --version
pre-commit 4.5.1 (reproduced); Z40 = '0' * 40 is still on line 14 of hook_impl.py on current main (f35134b) and in the 4.6.0 tag, so the bug is present on both.
.pre-commit-config.yaml
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v5.0.0
hooks:
- id: trailing-whitespace
~/.cache/pre-commit/pre-commit.log (if present)
### version information
pre-commit version: 4.5.1
git --version: git version 2.47.3
sys.version:
3.12.12 (main, Jan 16 2026, 00:00:00) [GCC 14.3.1 20250617 (Red Hat 14.3.1-2)]
sys.executable: /home/rlichten/.local/share/uv/tools/pre-commit/bin/python
os.name: posix
sys.platform: linux
### error information
An unexpected error has occurred: CalledProcessError: command: ('/usr/bin/git', 'diff', '--name-only', '--no-ext-diff', '-z', '<sha>..<64-zeros>')
return code: 128
stdout: (none)
stderr:
fatal: Invalid revision range <sha>..<64-zeros>
Traceback (most recent call last):
File ".../pre_commit/git.py", line 161, in get_changed_files
_, out, _ = cmd_output(*diff_cmd, f'{old}...{new}')
...
pre_commit.util.CalledProcessError: command: ('/usr/bin/git', 'diff', '--name-only', '--no-ext-diff', '-z', '<sha>...<64-zeros>')
return code: 128
stderr:
fatal: Invalid symmetric difference expression <sha>...<64-zeros>
search you tried in the issue tracker
sha256,SHA-256 push,sha-256 delete,Z40,object-format sha256,"Invalid revision range","bad object",zero SHA,pre-push delete,deletion pushThe closest related hit is #3434 (closed), but that is about fetching sha1-format hook repos into a sha256 local repo — a different codepath.
describe your issue
pre_commit/commands/hook_impl.pyhardcodes the SHA-1 zero OID:It is compared against
local_sha/remote_shato detect deletion-only pushes and new branches:In a repository initialized with
--object-format=sha256, git's pre-push hook protocol emits 64-character zero OIDs rather than 40. The equality never matches, the deletion-only short-circuit never fires, and pre-commit falls through intogit diff --name-only ... <old>..<64-zeros>(and then<old>...<64-zeros>). Both error out:pre-commit surfaces these as
An unexpected error has occurred: CalledProcessErrorand exits non-zero, sogit push origin --delete <branch>is refused on every sha256 repo. Branch deletion has to go through the forge's web UI or API to succeed.Minimal reproducer:
Suggested fix: accept an all-zero OID of either supported length (40 or 64), or (more general) read the repo's zero OID at runtime. A one-liner that covers both currently-supported object formats:
Callers become
if _is_zero_oid(local_sha): continueandelif not _is_zero_oid(remote_sha) and _rev_exists(remote_sha):. A strictly hash-agnostic alternative readsgit rev-parse --show-object-formatonce per hook invocation and computes the zero string from it.Happy to open a PR if helpful.
pre-commit --version
pre-commit 4.5.1 (reproduced);
Z40 = '0' * 40is still on line 14 ofhook_impl.pyon currentmain(f35134b) and in the 4.6.0 tag, so the bug is present on both..pre-commit-config.yaml
~/.cache/pre-commit/pre-commit.log (if present)