Two weeks ago I helped a teammate migrate a legacy service from Bitbucket Server to GitHub. The migration went fine. The post-mortem did not, because it turned out half our CI scripts parse commit hashes with a 40-character regex, and the new hashes are 64 characters. It took a full day to find every spot.
I am telling you this because Git 3.0 is about to make that problem everyone's problem. The next major version of Git will change the default hash algorithm for new repositories from SHA-1 to SHA-256. Git co-creator Scott Chacon, who co-wrote the Pro Git book and founded GitHub and GitButler, published a post this week titled Git 3.0's upcoming SHA-256 default will be a costly mistake, and it hit #3 on Hacker News with 549 points in under a day.
His claim: the switch is a "global train wreck" that buys almost no real security. The Git project's position: SHA-1 is deprecated by NIST and more attacks are coming, so the default has to move now while there is still runway. Both cannot be fully right, and the difference matters to every developer who will run git init after 3.0 ships.
I have not participated in the Git mailing list discussions or run the official migration tooling in production. Everything below comes from Chacon's post, the official Git BreakingChanges documentation, the hash-function-transition design doc, and the HN thread where both camps argued it out, all linked inline. I will flag what is verified fact versus contested claim.
What is actually changing
First, the facts nobody in the debate disputes, verified against the official docs.
- Only new repositories get the new default. The BreakingChanges doc says the default hash function for newly initialized repositories changes from "sha1" to "sha256". Existing SHA-1 repositories are not auto-converted and the doc states there is "no plan to deprecate the sha1 object format at this point in time".
-
It has not happened yet. As of Git 2.55, the current release, SHA-1 is still the default. SHA-256 kicks in only when Git is built with breaking changes enabled, which is the Git 3.0 mode. There is no release date for 3.0 yet. If you read somewhere that SHA-256 is already the default, that is wrong. You can verify with
git init && git rev-parse --show-object-format. - Mixed-format repositories are not possible. A repository is entirely SHA-1 or entirely SHA-256. You cannot mix the two in one repo, and submodules must match the parent repository's format, a constraint noted in the transition design doc.
- Hashes get longer. SHA-256 object IDs are 64 hex characters instead of 40. The visual change is immediate; the tooling impact is the real story.
That is the mechanism. Now the argument.
The case against the switch: Chacon's "Hashmageddon"
The threat it defends against is mostly theoretical. SHA-1 collision attacks exist. The SHAttered attack demonstrated one in 2017, and the 2020 "SHA-1 is a Shambles" paper pushed chosen-prefix collisions down to about 2^63 operations. Chacon's point is not that SHA-1 is fine. His point is about what the weakness can actually be used for in Git. To exploit it, an attacker needs to manufacture two file variants that hash identically, get victims to fetch the malicious one fresh without ever having pulled before, and run it. His math: brute-forcing a collision on 160-bit space would take roughly 16 billion years of GPU time for meaningful content, and the practical chosen-prefix cost runs "a few tens of thousands of dollars" for contrived content shapes. Meanwhile, he argues, if someone wants untrusted code into your project, buying out a burned-out open source maintainer for a $40k lump sum is "maybe a billion times simpler, cheaper and more likely to succeed" than a cryptographic attack. Real-world attacks today are social engineering, not hash collisions.
The migration cost lands on everyone. Every git init after 3.0 creates a repo that older tooling may choke on. His predictions:
- A new SHA-256 repo cannot push to hosts that do not support it. He shows GitHub currently cannot host SHA-256 repos, though it is in private beta according to HN commenters, and notes this gap is "probably the main thing currently delaying 3.0 entirely".
- Forge repo creation will need a format picker. Users must know which format to select before they understand what the question means.
- Every script, CI pipeline, issue tracker, and deploy tool that assumes a 40-character hash needs updating. He calls this list long, wide-ranging, and largely unsolved, with no consensus solution.
- Git's non-reentrant, unlinkable library status means most ecosystem tools are from-scratch reimplementations, and nearly none have full SHA-256 support. Anything not shelling out to the git binary will break on new-format repos.
- Existing projects that convert will break all existing signatures and every historical link that embeds an old hash.
His alternative: add verification without changing the keys. Chacon proposes independent tree hash headers. Instead of migrating, sign a second hash (SHA-256 or BLAKE3) of the full tree contents as an extra header inside the objects you sign. Content addressing stays SHA-1, which becomes just a database key, not a trust anchor. His proof of concept checksummed the Linux kernel tree, 1.5GB, in 257ms, and the Git project itself in 17ms. If SHA-256 is ever broken the same way, you add a tree-blake3 header instead of migrating the world again. This also solves the compliance problem, because NIST's 2030 deadline is about SHA-1 providing cryptographic protection, not existing anywhere in your stack. If signatures cover a SHA-256 content hash, SHA-1 is no longer protecting anything.
The case for the switch: why the Git project is doing it anyway
This is where most coverage of this debate stops, at Chacon's argument. But the official reasoning is in the BreakingChanges doc, and the HN thread surfaced counterpoints that Chacon's post does not fully engage.
The official reason is a security timeline, not a present attack. The docs cite Shambles (2020) chosen-prefix attacks at 2^63 operations, note Git has partial protections but "more attacks against SHA-1 will be found by future research", and conclude that with hardware growing cheaper, "it is only a matter of time before SHA-1 will be considered broken completely. We want to be prepared." The switch is deliberately early, while the ecosystem has years of runway, not during a live incident.
The "just add a second hash" plan has a hole. This is the strongest counterargument, and it came from the HN thread. A signature that covers a SHA-256 hash of the tree content protects the files. It does not protect the history structure. Commit graphs are chains of hash references, and those references are SHA-1. As one commenter put it, "I didn't introduce the vulnerability, he did! Look I can even prove it with his signed commit." An attacker can restructure what a commit claims to be built on, and the independent tree hash verifies the final snapshot but not the lineage it claims. To protect lineage, you need a parallel Merkle structure, which is most of the complexity of a full migration with a worse developer experience. Chacon concedes his approach may not let you "trust every commit, only the objects that have the header."
The transition is more graceful than the headline suggests. The hash-function-transition doc specifies a bidirectional mapping between SHA-1 and SHA-256 object names, so old hashes in docs, links, and signatures stay resolvable. A converted repository can interoperate with SHA-1 remotes by translating names. SHA-1-based signatures can be preserved and validated against the SHA-256 representation. One HN commenter who tested it confirmed that SHA-1 submodules inside a SHA-256 parent repo actually work on current Git. So the "dumpster fire" framing assumes the worst parts of the old experimental implementation.
The do-nothing path has its own cliff. Another counterpoint: leave SHA-1 as default and "nobody changes to sha256. sha1 is broken in 10 years. Suddenly everyone has to switch all at once on the same day because it is a critical security issue." Deferring a known-hard migration until it becomes an emergency is how the ecosystem got the SHA-1 deprecation mess it is trying to avoid.
There is a compliance layer too. SHA-1 is recommended against in FIPS 140-2 and similar certifications. For companies whose security teams blanket-ban SHA-1, keeping it as Git's default forever is not actually neutral. The switch is what makes Git pass procurement.
The part everyone can agree on
Strip the rhetoric and both sides share three premises:
- SHA-1 in Git has never been exploited in the wild in 20+ years, and the practical attack surface is small.
- The 40-to-64 character change will break a long tail of tooling that hardcodes hash length. This will cause real, distributed pain for years.
- Trust should come from signatures, not from the hash algorithm. The disagreement is only about whether that insight argues for not switching, or for switching now while the ecosystem still has time to fix its tooling.
What I would actually do
I run several small services with their own repos and automation scripts I barely remember writing. My plan, which I think generalizes to most working developers:
- Do nothing until Git 3.0 is actually released. Current Git 2.55 defaults to SHA-1. There is no date on 3.0. Panic conversion today buys nothing.
-
Find your 40-character assumptions now. Grep your CI configs, deploy scripts, hooks, and dashboards for
[0-9a-f]{40}and fixed 40-char parsing. Fix them to handle both lengths before anything forces the issue. This is cheap today and expensive during an incident. -
Keep one foot in reality. Try
git init --object-format=sha256in a scratch repo, run your normal tooling against it, and note what breaks. For most small projects, nothing will. - Do not convert active repos early. Conversion breaks signatures and old links. Wait for forge support to be general, the transition tooling to mature, and your hosting platform to make the format choice boring or automatic.
- Sign the things that matter. Whatever happens with the default, commit and tag signing is the trust mechanism both camps agree on. If your project's integrity matters, that is the highest-value move available today.
The Git 3.0 transition will be messy at the edges and invisible at the core for most developers. The debate worth following is not whether SHA-256 is coming, but whether the ecosystem's trust model moves from "the hash is the certificate" to "the signature is the certificate". Chacon's tree-hash proposal is the most interesting idea in the whole discussion precisely because it tries to make that shift without a decade of migration pain.
I write about developer tools, AI, and backend engineering every week. Subscribe, it is free.
What is your take: is the Git project doing responsible prep, or repeating the master-to-main fight at a much larger scale? Have you tested your tooling against SHA-256 repos yet?
Sources and further reading:
- Git 3.0's upcoming SHA-256 default will be a costly mistake by Scott Chacon
- Git BreakingChanges documentation, the official rationale
- Git hash-function-transition design doc, the migration mechanics
- Hacker News discussion, 387+ comments including Chacon himself
- NIST: Transitioning away from SHA-1
Top comments (0)