Git 3.0 will bring several breaking changes, and one of them will affect almost everyone even though it gets little attention: new repositories will use SHA-256 instead of SHA-1 to identify commits, trees and files. Scott Chacon, co-founder of GitHub and GitButler and author of Pro Git, has published a long and very critical article arguing that it will be an extremely expensive change for almost no benefit.
I read it carefully because, although the tone is a rant, the arguments are serious and there’s a concrete alternative proposal.
Why Git wants to drop SHA-1
Git is a content-addressable database: every object is stored with the hash of its content as the key. Each commit includes the hash of the previous one, so the hash of the latest commit “summarizes” the whole history. Since 2005 that hash has been SHA-1.
The problem is that SHA-1 has been considered broken since practical collisions were published: SHAttered in 2017 and SHA-1 is a Shambles in 2020. With enough GPUs you can deliberately craft two different files with the same hash. Git responded in 2017 with sha1dc, a version of SHA-1 that detects that kind of collision, and has since been preparing the transition to SHA-256, which you’ve been able to try for years:
git init --object-format=sha256 test
cd test
git rev-parse --show-object-format # sha256
In Git 3.0 that will become what git init does by default.
Chacon’s argument: the hash isn’t the trust
Chacon starts by separating two kinds of attack we tend to mix up:
| Attack | What the attacker does | Situation with SHA-1 |
|---|---|---|
| Collision | Crafts two files with the same hash, one benign and one malicious, and later swaps them | Possible, with a lot of money in GPUs |
| Second preimage | Crafts a file with the same hash as someone else’s existing file | Infeasible even with MD5 |
The truly dangerous attack would be the second one, and it isn’t feasible even with hashes much weaker than SHA-1. The collision attack requires the attacker to be the one who introduces the original file into the repository, and then to get someone to download the malicious version from a source other than the original.
And that’s the core of his argument, which Linus Torvalds already made when he created Git: security isn’t in the SHA-1, it’s in where you pull from. You trust a repository because you trust whoever maintains it and the authentication of whoever hosts it, not because of the signature on a hash.
Real attacks confirm it. Nobody spends tens of thousands of dollars on GPUs to slip in a binary with the same hash. What happens is that someone gets write access to an npm package used by millions of projects, or wins the trust of a tired maintainer and ends up inheriting the project. Compared to that, a collision attack is, in his words, “maybe the dumbest possible way” to get malicious code onto a system.
What it will cost
This is where I think Chacon is most right. The format change isn’t an internal detail:
- The two formats don’t mix. A repository is either SHA-1 or SHA-256, and the server has to know which when it’s created. Get ready to see a lot of
fatal: the receiving end does not support this repository's hash algorithm. - Submodules have to be the same type. A library that wants to serve projects in both formats will need two versions.
- Converting an existing repository rewrites all of it. Every hash changes, every signature breaks, and every link to a commit in issues, Slack, emails or documentation stops working.
- All code that assumes 40 characters for a hash will need reviewing: scripts, CI, internal tools, regular expressions…
- Alternative libraries lag behind. Since Git’s library is GPL-licensed and not designed to be linked, most tools use their own reimplementations (libgit2, gitoxide, JGit…), and their SHA-256 support is partial or missing.
He also cites a talk by Emily Shaffer about how Google is preparing, in which she says they might force SHA-1 internally on all new repositories to delay the change as long as possible. If Google is considering that, the rest of us are going to feel it.
His alternative: sign a second hash
The proposal is to keep SHA-1 as what it is in practice, a key to store and retrieve content, and not use it for trust. For projects that do need to verify content, when signing a commit or tag an independent hash (SHA-256, BLAKE3…) of the whole tree would be computed and added as a header of the signed object.
flowchart LR
A["Commit tree"] --> B["SHA-1 · storage"]
A --> C["SHA-256 of content · verification"]
B --> D["Signed tag or commit"]
C --> DThe signature would cover both, and to fool it you’d have to find content that collides in both algorithms at once. If SHA-256 ever falls, you add another algorithm without migrating anything. It’s not a new idea: git-evtag, by Colin Walters, has done something very similar since 2015 with SHA-512.
Chacon measured the cost with a proof of concept:
| Repository | Size | Time |
|---|---|---|
| Chromium with all its submodules | 35 GB, 2.1 million files | 5 s |
| Linux | 1.5 GB | 257 ms |
| Git | — | 17 ms |
In other words, you only pay when signing, and not much. It could also be applied retroactively, adding signed tags to old releases.
My caveats
I share much of the diagnosis, but there are things worth keeping in mind when reading it:
- Chacon isn’t neutral. GitButler is precisely one of those tools that doesn’t use the Git binary and will have to support both formats. That doesn’t make him wrong, but it explains some of the intensity.
- The collision attack isn’t that easy to dismiss in some contexts. The scenario of a contributor who earns trust for years and then swaps a file is exactly what happened with xz in 2024. No collision was needed there, true, but with a signed tag and a test binary swapped without leaving a trace in the history, it would have been even harder to detect.
- The transition has been designed for years on the Git mailing list, with a plan that includes interoperability between formats. Chacon acknowledges it at the end: almost all these arguments have been discussed there before. What he asks for is to stop and think before changing the default, not to throw the work away.
- NIST sets 2030 as the deadline for SHA-1 in cryptographic uses, and that weighs heavily on companies and public administrations. His answer is reasonable: if the signature also covers a SHA-256, SHA-1 no longer protects anything and becomes just a key.
What I’d do now
When Git 3.0 comes out, you don’t need to do anything with existing repositories: they remain SHA-1 and Git will keep handling them. What changes is git init. If you work with a server or tools that don’t support SHA-256 yet, you can set the default format:
git config --global init.defaultObjectFormat sha1
And to find out what type a given repository is, git rev-parse --show-object-format.
What I take away
I’m keeping the underlying distinction, which I think is the most valuable part of the article: integrity isn’t the same as trust. The hash guarantees nobody has accidentally corrupted the content; trust comes from who maintains the repository, who has access and who signs the releases. Mixing the two leads to huge migrations every time a new theoretical attack is published, while real attacks keep coming in through the supply chain.
I don’t know whether Git 3.0 will end up changing the default or not, but the debate is worth following, because when it arrives it’ll affect all of us.




