Git 2.29 Introduces SHA-256 Option, Negative Refspecs, and More
The open source Git project has released version 2.29, bringing contributions from over 89 developers, 24 of whom are new. This release introduces experimental support for a new object format, improves how remote refs can be filtered, and expands the grouping capabilities of git shortlog.
Experimental SHA-256 Support
Git 2.29 adds experimental support for storing repository objects using the SHA-256 hash algorithm instead of the traditional SHA-1. In Git's architecture, every object—whether a blob, tree, or commit—is identified by an object id computed from a hash of its content. The choice of hash function has security implications: while Git's SHA-1 collisions are statistically improbable in normal use, published attacks have demonstrated ways to generate collisions at significant computational cost, and detection mechanisms already exist in Git to reject objects that show signs of colliding pairs. However, any weakness in a cryptographic hash is a concern, pushing the project toward a more secure alternative.
$ git --version git version 2.29.0 $ git init --object-format=sha256 repo Initialized empty Git repository in /home/ttaylorr/repo/.git/ $ cd repo $ echo 'Hello, SHA-256!' >README.md $ git add README.md $ git commit -m "README.md: initial commit" [master (root-commit) 6e92961] README.md: initial commit 1 file changed, 1 insertion(+) create mode 100644 README.md $ git rev-parse HEAD 6e929619da9d82c78dd854dfe237c61cbad9e95148c1849b1f96ada5ee800810
With version 2.29, Git can operate in a full SHA-1 or full SHA-256 mode. Repositories using different object formats cannot currently interoperate with one another, though support is planned. Most major hosting providers, including GitHub, do not yet support SHA-256 repositories. Future releases will allow interoperability by computing both hashes for each object and maintaining a translation table between the formats, enabling SHA-256 repositories to work with sufficiently up-to-date SHA-1 clients and preserving references to older SHA-1 commits even after conversion.
Negative Refspecs Simplify Exclusions
Refspecs define which references a git fetch or git push should transfer. A default refspec, configured during clone, fetches all branches into the refs/remotes/origin/ hierarchy:
$ git config remote.origin.fetch +refs/heads/*:refs/remotes/origin/*
You can specify multiple refspecs for different purposes, such as fetching git notes alongside branches:
$ git config --add remote.origin.fetch refs/notes/commits:refs/notes/origin-notes
Prior to Git 2.29, refspecs could only include references you wanted. To exclude a pattern like refs/heads/ref-to-exclude, users had to list every other branch explicitly—an approach that quickly becomes unmanageable:
$ git ls-remote origin 'refs/heads/*' |
grep -v ref-to-exclude |
awk '{ print $2:$2 }' |
xargs git fetch origin
Beginning with this release, a refspec starting with ^ denotes a reference to exclude. A reference is sent if it matches at least one positive refspec and does not match any negative refspecs:
$ git fetch origin 'refs/heads/*:refs/heads/*' ^refs/heads/ref-to-exclude
Negative refspecs come with a few restrictions:
- They may contain wildcard patterns, but cannot specify a destination—for example,
^refs/heads/foo/*is valid, but not^refs/heads/foo/*:refs/heads/bar/*. - They can only refer to named refs, not raw object ids, unlike positive refspecs.
These negative patterns work equally well when placed directly in configuration files, so users who routinely want to exclude a particular branch—say, foo—can set that up once:
$ git config --add remote.origin.fetch ^refs/heads/foo
Expanded git shortlog Grouping
git shortlog summarizes history like git log, but groups commits by author rather than listing each commit individually. Previously, it only grouped by the commit's author or committer. If your project credits contributors using the Co-authored-by trailer, those co-authors were invisible to git shortlog—a limitation now lifted.
This release adds a --group option that accepts author (the default), committer (equivalent to -c), or a trailer-based grouping via --group=trailer:<field>. That opens up possibilities like identifying who reviews the most patches using the Reviewed-by trailer:
$ git shortlog -ns --group=trailer:reviewed-by v2.28.0.. | head -n5
40 Eric Sunshine
10 Taylor Blau
4 brian m. carlson
2 Elijah Newren
1 Jeff King
The option can be repeated, allowing a single run to count commits under multiple categories—such as crediting both authors and co-authors:
$ git shortlog -ns --group=author --group=trailer:co-authored-by
Combining --group with the --format option produces even more flexible views of your history:
$ git shortlog --format="...helped %an on %as" --group=trailer:helped-by v2.28.0..v2.29.0
Chris Torek (3):
...helped René Scharfe on 2020-08-12
...helped René Scharfe on 2020-08-12
...helped René Scharfe on 2020-08-12
David Aguilar (1):
...helped Lin Sun on 2020-05-07
Denton Liu (1):
...helped Shourya Shukla on 2020-08-21
Derrick Stolee (2):
...helped Taylor Blau on 2020-08-25
...helped Taylor Blau on 2020-09-17
[...]
Ref-query refinements
git for-each-ref picks up several improvements in this release. The --format option gains new fields, including contents:size and subject:sanitize, plus more consistent :short modifiers for abbreviated object ids. Separately, the command now accepts multiple --merged and --no-merged arguments: a ref is shown if it is reachable from at least one --merged argument and not reachable from any --no-merged one.
Merge messages and checkout behavior
When a git merge hits a conflict, the default message can be genuinely confusing. Consider this example:
CONFLICT (rename/delete): foo.c deleted in b01dface... Removed unnecessary stuff and renamed in HEAD. Version HEAD of foo.c left in tree.
Prior to Git 2.29, there was no way to tell whether Git had deleted files and renamed others in HEAD, or whether “Removed unnecessary stuff” was simply a commit subject. The latter was the case, but the message gave no hint. Starting with this version, Git wraps the commit subject in parentheses, making it clear which part of the conflict notice came from a commit and which was generated by Git.
There is also a fix involving merge.renormalize, which makes Git check out and check in each stage of a three-way merge to handle line-ending differences. That setting was previously ignored by git checkout -m and some related commands; it is now honored correctly.
Protocol v2 back by default
Protocol v2, which became the default in Git 2.26, was pulled back to “v0” in 2.27 after a bug was found in the new implementation. The feature was marked experimental in 2.28. With several releases of bug fixes behind it, protocol v2 is once again the default in Git 2.29.
Bisecting along the first parent
git bisect can now take a --first-parent option, which changes the set of commits traversed between the good and bad endpoints. Previously, a bisection through a merge would consider commits that came in from the merged branch individually. With --first-parent, Git skips those side-branch commits entirely. That is useful when the mainline merge commits are the only interesting stopping points — for example, if you work primarily by merging pull requests and individual commits inside those requests may not even build.
Security fix and performance work
A vulnerability in the optional MediaWiki remote backend for push and pull could lead to arbitrary command execution. It is now patched. The backend is not compiled by default and is generally unsupported; the risk applies only if you use it against an untrusted MediaWiki instance.
The low-level git index-pack command, used for receiving pushes and processing fetches, has also been made more efficient on multi-core machines. That means pushes and fetches should become faster simply by upgrading.
Configurable merge commit subjects
Default merge commit messages conventionally read “Merge $upstream into $dest”, with the “into $dest” portion omitted when merging into the main branch. The new multi-valued merge.suppressDest config option lets you specify any branch names that should trigger that truncation.
More in the release
These are only a selection of the changes in Git 2.29. Full details are available in the release notes for this version and for any previous Git release in the repository.



