autoupdate --freeze uses git rev-parse <tag> to resolve the frozen SHA, but for annotated tags this returns the tag object SHA, not the underlying commit SHA.
This means the pinned SHA in .pre-commit-config.yaml ends up being a tag object rather than a commit. It still works at checkout time (git dereferences tag objects transparently), but it causes a few problems:
- The SHA is not browsable on GitHub.
https://github.com/<org>/<repo>/commit/<tag-object-sha> returns 404 because it's not a commit. This makes it harder to audit what code a frozen rev actually points to.
- Tools like Dependabot that also resolve frozen pre-commit revs will resolve the same tag to the commit SHA instead, producing PRs that change the SHA without changing the version (e.g.
# frozen: 1.20.0 stays the same but the rev changes). This is confusing for reviewers.
- Compare URLs on GitHub also 404 when one side is a tag object SHA, so you can't easily diff what changed.
To reproduce, run pre-commit autoupdate --freeze on a config that uses a repo with annotated tags (e.g. blacken-docs, check-jsonschema), then check the resulting SHA:
$ git cat-file -t <frozen-sha>
tag # <-- should be "commit"
The fix should be straightforward — in pre_commit/commands/autoupdate.py, the freeze logic:
exact = cmd_output(*_git, 'rev-parse', rev)[1].strip()
should dereference through tag objects:
exact = cmd_output(*_git, 'rev-parse', f'{rev}^{{}}')[1].strip()
^{} tells git to peel the ref until it reaches a non-tag object (i.e. the commit).
Found this while investigating conda/conda#15820 where Dependabot was showing SHA changes for blacken-docs (1.20.0 → 1.20.0) and check-jsonschema (0.37.1 → 0.37.1). The old SHAs from --freeze were annotated tag objects, the new SHAs from Dependabot were the commits those tag objects point to. Same code, different object types.
For example, for blacken-docs 1.20.0:
dda8db18cfc68df532abf33b185ecd12d5b7b326 — the annotated tag object (what --freeze pinned, 404s on GitHub)
fda77690955e9b63c6687d8806bafd56a526e45f — the commit it points to (what Dependabot resolved to, works on GitHub)
Both resolve to the same tree, confirmed via git rev-parse <sha>^{tree}.
autoupdate --freezeusesgit rev-parse <tag>to resolve the frozen SHA, but for annotated tags this returns the tag object SHA, not the underlying commit SHA.This means the pinned SHA in
.pre-commit-config.yamlends up being a tag object rather than a commit. It still works at checkout time (git dereferences tag objects transparently), but it causes a few problems:https://github.com/<org>/<repo>/commit/<tag-object-sha>returns 404 because it's not a commit. This makes it harder to audit what code a frozen rev actually points to.# frozen: 1.20.0stays the same but the rev changes). This is confusing for reviewers.To reproduce, run
pre-commit autoupdate --freezeon a config that uses a repo with annotated tags (e.g.blacken-docs,check-jsonschema), then check the resulting SHA:The fix should be straightforward — in
pre_commit/commands/autoupdate.py, the freeze logic:should dereference through tag objects:
^{}tells git to peel the ref until it reaches a non-tag object (i.e. the commit).Found this while investigating conda/conda#15820 where Dependabot was showing SHA changes for
blacken-docs(1.20.0 → 1.20.0) andcheck-jsonschema(0.37.1 → 0.37.1). The old SHAs from--freezewere annotated tag objects, the new SHAs from Dependabot were the commits those tag objects point to. Same code, different object types.For example, for
blacken-docs1.20.0:dda8db18cfc68df532abf33b185ecd12d5b7b326— the annotated tag object (what--freezepinned, 404s on GitHub)fda77690955e9b63c6687d8806bafd56a526e45f— the commit it points to (what Dependabot resolved to, works on GitHub)Both resolve to the same tree, confirmed via
git rev-parse <sha>^{tree}.