Stackness
Git SHA-256 repository compatibility: test your stack before Git 3.0 makes it the default

Git SHA-256 repository compatibility: test your stack before Git 3.0 makes it the default

Git 3.0 will create new repositories with SHA-256 object IDs instead of SHA-1, and as of 7 October 2026 no released Git can push or fetch between the two formats. The five steps below use one test repository and end with the list of forges, Git libraries and CI jobs in your stack that break. I ran them on Git 2.50.1 and 2.56.0 on 7 October 2026: 6 of the 11 Git library versions and tools I tested could not open a SHA-256 repository, and the git init then fetch sequence CI checkouts use failed under a simulated Git 3.0 default.

What changes when Git 3.0 defaults to SHA-256?

Git's BreakingChanges document switches the default for "newly initialized repositories" to SHA-256 in Git 3.0. Existing repositories keep SHA-1, and git clone keeps whatever format the remote uses. Git sets no date ("There is no planned release date") and makes the switch conditional on "popular Git libraries, applications and forges" being ready.

SHA-256 support shipped in Git 2.29 on 19 October 2020. Almost six years on, Git's own docs still say "there is no interoperability between SHA-256 repositories and SHA-1 repositories": every cross-format push and fetch fails in every release up to 2.56.0 of 28 September 2026. Git 3.0 also makes Rust a build requirement.

On 1 October, Scott Chacon, a GitHub co-founder and now CEO of GitButler, argued that the new default "will be a costly mistake". The Hacker News thread reached 577 points and 536 comments by 7 October, with Chacon answering in it, and Lobsters added 73 more. GitButler sells Git tooling and is still shipping its own SHA-256 support.

Which tools can read a SHA-256 repository today?

As of 7 October 2026, Gitea and Forgejo host SHA-256 repositories, GitLab offers them as an experiment behind a flag, GitHub serves a few with no public way to create one, and Bitbucket and SourceHut do not. Among libraries, go-git v6 beta, gitoxide, Dulwich and anything that shells out to git work; libgit2, JGit, go-git v5 and isomorphic-git do not.

Forge SHA-256 Evidence
GitHub Serves some repos, none creatable by the public The REST hash-algorithm endpoint returns sha256 for at least one public repo; no announcement
GitLab Experiment, flag support_sha256_repositories Since 16.7, "not ready for production use" (docs)
Gitea Yes Since 1.22, May 2024; push and clone tested on 28.1.0
Forgejo, Codeberg Yes, "some features are still unreliable" Since Forgejo v7.0, April 2024
Bitbucket No Atlassian tickets at "Gathering Interest"
SourceHut No "mostly blocked on support from go-git"
Library, CI or tool SHA-256 Version checked
go-git v6 beta only v5.19.3 fails, v6.0.0-beta.1 works (tested)
libgit2 Not in default builds 1.9.7 fails (tested); on by default only in the unreleased v2.0
JGit No, and it cuts IDs to 40 characters 7.8.0 (tested)
gitoxide Read and clone gix 0.59.0 (tested)
Dulwich Yes, from 1.2.16 1.2.17 (tested)
GitPython Only through repo.git 3.2.0 (tested)
isomorphic-git No 1.43.1 (tested)
simple-git Yes, it shells out to git 4.0.2 (tested)
Git LFS Yes 3.8.0 (tested)
git-filter-repo No, it crashes 2.47.0 and main (tested)
actions/checkout in GitHub Actions Yes v6.0.3, 2 June 2026
GitLab Runner Yes, for GitLab projects 16.11
Jenkins git plugin No JENKINS-73504, open
VS Code Git Broken: copies 40 of 64 characters Issue #227281, open
Dependabot, Renovate Code assumes 40-character IDs Read in their source; no issue filed

On Stackness, as of 7 October 2026, 9 real profiles list GitHub, 4 list Git and 1 lists GitLab (data sources), small numbers that the DevOps and infrastructure tools developers list will update.

What you need

Testing for SHA-256 needs a recent Git, somewhere to push a test repository and the list of Git libraries your code loads. Any Git since 2.29 creates SHA-256 repositories, but older versions lack the setting that pins the default and the guard against mixed-format submodules.

  • Git 2.47 or later, and 2.53 or later for the submodule guard.
  • A scratch project on your forge, or a local Gitea in Docker.
  • Your CI runner image and the list of Git libraries your code loads.

1. Create a SHA-256 test repository

A SHA-256 test repository is the fixture for every later step: create it with --object-format=sha256 and one commit. The format is fixed at creation, and git init refuses to switch an existing repository with fatal: attempt to reinitialize repository with different hash.

git init --object-format=sha256 repo256 && cd repo256
echo test > README && git add README && git commit -q -m "First commit"
git rev-parse --show-object-format
git log --format=%H -1

Expect sha256 and a 64-character commit ID:

sha256
4b9e512ed827d903239c6182bc131e5d14ce4933c3c80836976e7ca2c1cee4d6

2. Push it to your forge

Create the remote project as SHA-256 before you push: push-to-create makes a SHA-1 repository on Gitea, Forgejo and GitLab, and the push fails. On Gitea, pass "object_format_name": "sha256" to the create-repo API; on GitLab, tick "Use SHA-256 as the repository hashing algorithm" under the experimental settings.

git remote add origin https://forge.example/you/test256.git
git push origin main

A forge or repository without SHA-256 answers fatal: the receiving end does not support this repository's hash algorithm. On GitHub, this call shows the format of any public repository:

curl https://api.github.com/repos/{owner}/{repo}/hash-algorithm

3. Open the repository with every Git library you ship

Every Git library your services, hooks and bots load has to open the test repository and read HEAD. Libraries without SHA-256 support fail with recognisable messages, and JGit also returns a shortened ID without an error. The messages from the test runs:

Library What it prints
go-git v5 unknown extension: objectformat
libgit2, through pygit2 unknown object format 'sha256'
JGit Missing unknown 4b9e512e..., while jgit rev-parse HEAD exits 0 with a 40-character ID
GitPython ValueError: Failed to parse reference information from 'refs/heads/main'
isomorphic-git NotFoundError - Could not find 4b9e512e...
git-filter-repo ValueError: invalid literal for int() with base 10

Then search your own code for IDs assumed to be 40 characters long:

git grep -nE '\{(7,)?40\}|[=*] ?40([^0-9]|$)'

4. Rehearse the Git 3.0 default on a CI runner

Turn on the new default on a runner and fetch a repository you already have. A job that runs git init and then git fetch breaks first, because the fresh repository is SHA-256 and every existing remote is SHA-1.

git config --global init.defaultObjectFormat sha256
git init -q g3 && cd g3
git remote add origin https://forge.example/you/existing-repo.git
git fetch origin main

Expect fatal: mismatched algorithms: client sha256; server sha1, while a git clone of the same remote works, because clone takes the remote's format. By my reading of its source, actions/checkout passes --object-format=sha256 only for repositories GitHub reports as SHA-256 and relies on the default otherwise. pre-commit hit the same error. Undo with git config --global --unset init.defaultObjectFormat.

5. Pin SHA-1 until the list is clean

Pin SHA-1 for new repositories on every runner image and workstation until steps 2 to 4 pass, so Git 3.0 changes nothing until you choose to. Three settings do it: the command-line flag beats GIT_DEFAULT_HASH, which beats the config setting.

git config --global init.defaultObjectFormat sha1   # Git 2.47 and later
export GIT_DEFAULT_HASH=sha1                         # overrides the config
git init --object-format=sha1 repo                   # overrides both

Convert an existing repository as a copy

No in-place conversion exists, and GitLab's docs say Git "does not support migrating to SHA-256 later, or migrating back to SHA-1". The route is git fast-export into a new SHA-256 repository, which changes every commit ID and drops signatures by default.

git init --object-format=sha256 conv256
git -C src fast-export --all --signed-tags=strip | git -C conv256 fast-import --quiet

What the conversion cost in testing:

  • Every ID changed, and commit messages citing an old ID kept the dead SHA-1 string.
  • Signatures were dropped without a warning; --signed-commits=verbatim copies them into commits that never verify, because SHA-256 repositories sign under gpgsig-sha256.
  • Submodules must convert with their superproject: Git 2.53 and later refuse mixed formats, and older versions write broken links silently.
  • git-filter-repo cannot rewrite the result yet.

Check that it worked

A stack is ready for SHA-256 when the test repository survives a push and a clone, every library in your list opens it, and new repositories on pinned runners stay SHA-1 until you lift the pin. Four checks confirm it:

  • git rev-parse --show-object-format prints sha256 in the test repository and sha1 in a new repository on a pinned runner.
  • git fsck --no-progress exits 0 after every push and clone.
  • git log --format=%H -1 | wc -c prints 65: 64 characters and a newline.
  • Every library in your list opened the test repository, or has an open ticket against it.

What goes wrong

Three errors cover most SHA-256 failures, and all three share one cause: a repository in one format talking to a remote in the other. Each is listed below by the message Git prints, with its cause and fix.

"the receiving end does not support this repository's hash algorithm"

The target forge or repository is SHA-1. Create it as SHA-256 first, or keep the repository on SHA-1; no Git release converts on the wire.

"mismatched algorithms: client sha256; server sha1"

A SHA-256 repository, usually a fresh git init under the new default, is fetching from a SHA-1 remote. Pin SHA-1 on the runner, or pass --object-format=sha1 where the tool runs git init.

"couldn't find remote ref" followed by a 64-character ID

An older checkout ran a plain git init and then fetched a SHA-256 commit (actions/checkout #1843). Upgrade to actions/checkout v6.0.3 or later, or set GIT_DEFAULT_HASH: sha256 in the job's environment.

SHA-256 readiness checklist

The checklist covers steps 1 to 5 and the conversion notes, in the order to run them. Copy it into the ticket that tracks Git 3.0 readiness, and tick an item only when its command passed on your own forge and runners.

[ ] git rev-parse --show-object-format run in every repository you own
[ ] test repository created as SHA-256 on the forge, pushed and cloned back
[ ] every Git library opened it (go-git v6, not v5; no libgit2 or JGit paths)
[ ] git grep for 40-character ID assumptions done
[ ] CI rehearsal with init.defaultObjectFormat=sha256 passed
[ ] actions/checkout at v6.0.3 or later
[ ] runner images and workstations pin sha1 until the list is clean
[ ] submodules and signing planned to move together

The pin belongs in the global Git config you already keep under version control, the dotfiles in git move, so every machine and runner image picks it up from one place.

Tools in this post

and 22 more from this post

Use any of these tools?

Put them on a Stackness profile, say how you use each one and see who pairs them the same way. It takes a couple of minutes.

Show my stack