⏱️ Reading time: 14 min

As of 2026, Git still uses SHA-1 by default to identify every commit, blob, and tree, even though that algorithm has been flagged as cryptographically broken since 2017. Scott Chacon, cofounder of GitButler, published a sharp critique of the Git 3.0 plan. According to his argument, adopting SHA-256 in Git as the default hash format would produce repositories that GitHub and GitLab still can’t host, with no practical benefit for most projects.

📑 En este artículo
  1. TL;DR
  2. What Is SHA-256 in Git?
  3. Why SHA-1 Stopped Being Enough
  4. How the Hash Change Works in Git
    1. What Changes in Object Size
    2. Compatibility and Interoperability Today
    3. Why SHA-256 and Not Another Algorithm
  5. Practical Examples: Testing SHA-256 on Your Machine
    1. Creating a New Repository with SHA-256
    2. Computing a SHA-256 Hash of a File
  6. Getting Started: Testing a SHA-256 Repository Step by Step
  7. Real Use Cases for the SHA-256 Format
  8. Common Mistakes When Testing the SHA-256 Format
  9. Comparison: SHA-1 vs. SHA-256
  10. Deep Dive: The Official Hash Transition Plan
  11. Frequently Asked Questions
    1. Can I migrate an existing SHA-1 repository to SHA-256 in Git without losing history?
    2. Do GitHub or GitLab accept repositories with the SHA-256 algorithm?
    3. What happens to a Git submodule if its hash format differs from the superproject’s?
    4. When will SHA-256 become Git’s default hash format?
    5. How do I check if a local repository uses SHA-1 or SHA-256?
    6. Does the SHA-256 algorithm make verifying signed commits slower?
  12. References

The controversy makes for a good hook, but the underlying issue is technical: what that algorithm actually changes, how compatible it is with the current ecosystem, and how to test it today on your own machine.

TL;DR

  • Git has supported SHA-256 experimentally since version 2.29 via git init --object-format=sha256.
  • GitHub and GitLab still don’t host SHA-256 repositories: there’s no direct git push to those services today.
  • The hash goes from 40 to 64 hexadecimal characters, which slightly increases the size of trees and commits with many references.
  • There’s no official conversion that preserves hashes when migrating an existing SHA-1 repository to SHA-256.
  • git rev-parse --show-object-format confirms in seconds which algorithm any local repository uses.

What Is SHA-256 in Git?

SHA-256 in Git is the alternative hash algorithm to SHA-1 that the project’s official documentation defines as the new 256-bit object format, capable of identifying every commit, tree, and blob with a wider cryptographic margin, and that any repository can adopt today experimentally at the moment of initialization.

The project documents this in its technical transition plan, hash-function-transition, published within the Git repository itself. That document explains why SHA-1 no longer suffices as an integrity guarantee and what steps the project plans to take to migrate without breaking the history of millions of existing repositories.

SHAttered, the first practical SHA-1 collision, was published in 2017. Foto de National Cancer Institute en Unsplash

Why SHA-1 Stopped Being Enough

Git is what’s called a content-addressable database: every file, every directory tree, and every commit is identified by the hash of its content, not by an arbitrary number. That property is what gives it integrity. Changing a single byte in an old commit changes its hash, and that change propagates to every subsequent commit because each one includes the hash of its parent.

Since Linus Torvalds chose SHA-1 in 2005, that design has worked without any known accidental collision incidents. The problem isn’t chance, it’s deliberate attack. In 2017, the Google and CWI Amsterdam team published SHAttered, the first practical SHA-1 collision: two different PDF files with the same hash. In 2020, the paper SHA-1 is a Shambles made the attack even cheaper, to the point of making it viable with GPUs rented in the cloud instead of dedicated supercomputers.

That doesn’t mean a Git commit could easily be forged tomorrow: a practical attack against a real repository still requires specific conditions in the manipulated content. But it does mean SHA-1 no longer meets the standard that modern cryptography demands for an integrity hash, and that Git needs a replacement before the attack window gets any cheaper.

How the Hash Change Works in Git

Git doesn’t swap one algorithm for another within the same repository. Instead, it defines a new repository format. When you run git init --object-format=sha256, Git creates a .git folder whose config file includes the [extensions] objectFormat = sha256 section. That flag tells any version of Git that opens that repository to use SHA-256 for everything: computing blob hashes, building trees, signing commits, and writing the object pack.

What Changes in Object Size

The most visible difference is hash length. SHA-1 produces 160 bits, represented as 40 hexadecimal characters; SHA-256 produces 256 bits, represented as 64 characters. A commit or a tree that references other objects stores those references as text, so every internal reference grows from 40 to 64 bytes.

The effect is more noticeable in trees with many files and in long histories. A monorepo with tens of thousands of files per commit sees the size of each tree object grow in direct proportion to that 24-byte difference per entry. For an individual blob, on the other hand, the change is negligible, because the file’s content doesn’t change, only its identifier.

Compatibility and Interoperability Today

This is the technical core of GitButler’s critique. A SHA-1 repository and a SHA-256 one aren’t interchangeable: you can’t run git push or git pull between them without going through a conversion, and Git still doesn’t include a tool in its stable branch that preserves the original hashes when migrating.

The official hash-function-transition document describes the design meant to solve this: a translation table that maps every SHA-1 identifier to its SHA-256 equivalent and vice versa, so a repository can speak both formats during the transition. That mechanism remains, for the most part, a documented plan rather than a production-ready feature across all workflows.

flowchart TD
    A["SHA-1 Repository"] -->|"git push / git pull"| B["SHA-1 Remote, GitHub or GitLab"]
    C["SHA-256 Repository"] -.->|"no native support today"| B
    C -->|"git push / git pull"| D["SHA-256 Remote, self-hosted"]
    subgraph Hosting available in 2026
    B
    end
    subgraph Experimental hosting
    D
    end

Why SHA-256 and Not Another Algorithm

The transition document itself explains the selection criteria. SHA-256 is an already-validated NIST standard, with mature implementations across every cryptographic library and hardware acceleration available on modern processors through Intel and ARM’s SHA extensions. Alternatives like BLAKE2 or SHA-3 also met the required security level, but prioritizing an already-ubiquitous algorithm reduced adoption friction across the entire ecosystem of tools surrounding Git.

Practical Examples: Testing SHA-256 on Your Machine

You don’t have to wait for Git 3.0 to experiment. Support for --object-format=sha256 has existed since Git 2.29, released in 2020, so any recent installation already includes it. The only thing that changes with Git 3.0 is that it would stop being an explicit option and become the default value.

Creating a New Repository with SHA-256

git init --object-format=sha256 repo-sha256
cd repo-sha256

Git responds with the standard initialization confirmation:

Initialized empty Git repository in /home/dev/repo-sha256/.git/

To confirm which algorithm is active, without relying on memory, Git exposes a dedicated command:

git rev-parse --show-object-format

The output is a single word with the algorithm’s name:

sha256

If you run the same command on a repository created without the --object-format flag, the output is sha1: that’s today’s default value in any standard Git installation, the same one GitButler doesn’t want to see replaced without a resolved interoperability path.

Computing a SHA-256 Hash of a File

Within that same repository, any normal operation uses SHA-256 transparently. git hash-object makes it explicit:

echo "hello world" | git hash-object --stdin

In a SHA-1 repository that line returns a 40-character hexadecimal identifier. In a SHA-256 one, like the one you just created, the result has 64 characters: double the length, even though the hashed content is exactly the same.

sequenceDiagram
    participant U as User
    participant G as Git
    participant O as Object Database
    U->>G: git hash-object --stdin -w
    G->>G: computes the hash according to the repository's object-format
    G->>O: stores the blob object
    O-->>G: confirms the write
    G-->>U: returns the hash, 40 or 64 hex characters
⚠️ Heads up: a SHA-256 repository works perfectly on your machine, but today it’s an island: you can’t clone it or push it to GitHub, GitLab, or most of the forges you use in your daily work.

Getting Started: Testing a SHA-256 Repository Step by Step

You’ll need Git 2.29 or newer; any maintained Linux distribution in 2026 already meets this. Check your version before continuing:

git --version

If the result is lower than 2.29, update the git package with your Linux distribution’s package manager before continuing (on macOS, brew upgrade git; on Windows, download the installer from git-scm.com).

  1. Create the repository with the new format: git init --object-format=sha256 demo-sha256.
  2. Go into the folder and confirm the active algorithm: cd demo-sha256 && git rev-parse --show-object-format. It should print sha256.
  3. Add a file and make the first commit like in any repository: echo "demo" > file.txt && git add file.txt && git commit -m "first SHA-256 commit".
  4. Inspect the hash of the commit you just created with git log --format=%H -1: it will show 64 hexadecimal characters instead of the usual 40.

What you won’t be able to do yet, at least with most hosting services available in 2026, is push that repository to a remote on GitHub or GitLab: neither currently accepts the SHA-256 object format in its hosted repositories.

Git uses 64 hexadecimal characters for every object in the SHA-256 format. Foto de Trnava University en Unsplash
💡 Tip: if you need extra precision, git rev-parse --show-object-format=storage indicates the format objects are stored in on disk, which is different from the input or output format a specific command might use.

Real Use Cases for the SHA-256 Format

For now, the SHA-256 format makes sense in specific scenarios, not as a general replacement for everyday workflow.

  • Security teams that need to assess, before any corporate migration, how their internal tooling (hooks, CI, automation scripts) behaves with 64-character hashes.
  • Contributors to the Git project itself who test and report bugs on the translation table and the protocol support documented in hash-function-transition.
  • Environments with strict regulatory compliance, where internal policy requires avoiding SHA-1 in any system, including version control, regardless of whether a practical attack has been demonstrated against Git specifically.
  • Maintainers of self-hosted forges (Gitea, Forgejo, plain Git servers over SSH) who can host SHA-256 repositories without depending on GitHub or GitLab adding support first.

Common Mistakes When Testing the SHA-256 Format

The most common mistake is assuming it’s enough to run git init --object-format=sha256 on a repository that already exists. That command only works for new, empty repositories: it doesn’t convert an existing SHA-1 history, because changing the hash algorithm changes every object’s identifier, which breaks the integrity chain Git built commit by commit.

The second mistake is assuming a hosting provider will accept the push without any warning. In practice, the attempt fails immediately, or the service doesn’t even offer the option to create a remote repository in that format.

The third mistake is mixing submodules with different formats without checking first. A SHA-256 superproject that references a SHA-1 submodule, or vice versa, doesn’t currently have an officially supported end-to-end path. It’s best to keep the entire submodule tree in the same format while the ecosystem finishes maturing.

As a best practice, evaluate the technical impact on a disposable test repository, never on a copy of a production project, and document which tools in your pipeline (linters, pre-commit hooks, CI integrations) assume 40-character hashes somewhere in their code.

Comparison: SHA-1 vs. SHA-256

AspectSHA-1 (today’s default)SHA-256 (experimental)
Hash length160 bits, 40 hex characters256 bits, 64 hex characters
Cryptographic statusPractical collision demonstrated (SHAttered, 2017)No known practical collision
Creation commandgit init (no flags)git init --object-format=sha256
GitHub/GitLab supportFullNot available in 2026
Interoperability between formatsNot applicableNo official conversion that preserves hashes
Relative size of objects with many referencesBaselineSlightly larger due to the longer hash

Deep Dive: The Official Hash Transition Plan

The hash-function-transition document doesn’t describe a simple algorithm swap: it describes a coexistence period. The underlying idea is that a repository can store objects with their native hash (SHA-1 or SHA-256) while also maintaining a table that translates each identifier into the other format, so tools and protocols that still expect SHA-1 keep working while the ecosystem migrates.

That translation table is different from a simple history rewrite. Rewriting history with something like git filter-repo generates new commits, with new parents and new hashes, which changes every commit’s identity. The translation table, instead, aims for the same logical commit to have two valid, equivalent identifiers, one per format, without changing its content or its position in the history.

flowchart LR
    A["Commit, 40-hex SHA-1 hash"] --> T["Translation table"]
    B["Commit, 64-hex SHA-256 hash"] --> T
    T --> C["Bidirectional per-object mapping"]
    subgraph Interoperability design
    T
    C
    end

The repository also uses the extensions.objectFormat field to mark the repository’s format version as 1 instead of 0, a deliberate decision. This way, older Git versions that don’t understand SHA-256 reject the repository instead of silently corrupting it. In this design, backward compatibility is resolved by failing fast with a clear message instead of silently corrupting data.

Another detail the project itself documents is that GPG-signing commits and tags requires format consistency: a signed SHA-256 commit includes that 64-character hash inside the signed object, so external tools that parse signatures, for instance to verify them in a CI pipeline, need to be updated to accept the new length before they can validate that type of commit.

The impact isn’t limited to Git itself. Any tool that has assumed, directly or indirectly, that a Git hash always has 40 characters (CI scripts, log parsers, third-party integrations, custom hooks) needs a review before SHA-256 stops being a marginal option. That is, at bottom, the real technical concern behind GitButler’s critique. The cost of adaptation falls on thousands of projects that never asked for the change, even though SHA-256 itself is a solid cryptographic choice.

Your next step: clone an empty test repository, run git init --object-format=sha256, and compare with your own eyes the length of a commit’s hash against that of any SHA-1 repository.

📬 Get new articles by email

We only email about big articles (1-2 a month).

Frequently Asked Questions

Can I migrate an existing SHA-1 repository to SHA-256 in Git without losing history?

Not directly. Git still doesn’t include a stable tool that converts a SHA-1 repository to SHA-256 while preserving each commit’s original hashes. The official plan to achieve this, based on a translation table, remains documented as a design rather than a production-ready feature across all workflows.

Do GitHub or GitLab accept repositories with the SHA-256 algorithm?

No, at least not in 2026. Neither service currently offers the option to host a repository with extensions.objectFormat = sha256, which limits this format’s practical use to local repositories or those hosted on self-hosted forges.

What happens to a Git submodule if its hash format differs from the superproject’s?

There’s no officially supported path for mixing formats between a superproject and its submodules. The practical recommendation is to keep the entire submodule tree on the same algorithm while interoperability support remains incomplete.

When will SHA-256 become Git’s default hash format?

The Git community itself is debating the timeline. The GitButler post that prompted this piece claims the change is planned for a future 3.0 version, but that date depends on interoperability with the major hosting services advancing first.

How do I check if a local repository uses SHA-1 or SHA-256?

Run git rev-parse --show-object-format inside the repository. The output is a single word, sha1 or sha256, unambiguously.

Does the SHA-256 algorithm make verifying signed commits slower?

Not in any noticeable way for an individual developer. The extra cost of computing and verifying a SHA-256 hash instead of SHA-1 is minimal on modern hardware. The real bottleneck today is the lack of support in external tools, not the algorithm’s performance.

References

📱 Do you like this content? Follow @programacion on Telegram for daily tech content in Spanish: quick summaries, fresh content every day.

Featured image: Foto de Rahul Mishra en Unsplash

Did it work for you? Got a different error? Say so below: questions get answered and help the next reader.

Leave a comment
Categories: Tech NewsTutorials

Andrés Morales

Developer and AI researcher. Writes about language models, frameworks, developer tooling, and open source releases. Covers ML papers, the tech startup ecosystem, and programming trends.

0 Comments

Leave a Reply

Avatar placeholder

Your email address will not be published. Required fields are marked *

You can include code inside <code>…</code> or, for several lines, <pre><code>…</code></pre>.

This site uses Akismet to reduce spam. Learn how your comment data is processed.