Sparse index support arrives

Git 2.34, released with contributions from over 109 developers (29 of them first-timers), introduces a long-awaited change for those working in very large repositories. Previous releases added partial clones and sparse-checkout, which let you fetch only a subset of objects and populate only certain directories in your working copy. The idea was to make a monorepo feel like a much smaller repository. That illusion, however, was broken by the index—the data structure Git uses to track the state of every file and what will be written on the next commit. Even with a sparse checkout, the index still had to record the entire repository, so every index operation carried the full overhead of the repository’s total size.

Git 2.34 changes this with a sparse-enabled index. The index now understands specially marked directories as boundaries between the files you have checked out and those you don’t. Instead of tracking every file, it only contains entries for files inside your sparse checkout and for these boundary directories. Commands that need the complete picture can expand this sparse index back into a full one when necessary. For a thorough walkthrough of the implementation details and how the format was taught to Git’s subcommands, Derrick Stolee published a dedicated post on making monorepos feel small with the sparse index.

Collapsing to a sparse index
Triangles represent trees and boxes represent blobs. Left: a representation of a non-sparse index’s contents. Right: a sparse-ified index.

Bitmaps that span multiple packs

Reachability bitmaps help a Git server respond to fetches by quickly computing the set of objects the client needs: everything reachable from the requested refs minus what the client already has. A .bitmap file stores one bit per object for each commit, but until now those bitmaps were tied to the object ordering within a single packfile. That restricted bitmaps to repositories that could be compressed into one pack, which is impractical for very large repositories where the effort to write a single pack is substantial.

This release adds a new bitmap format based on the multi-pack index (multi-pack-index) file. Bitmaps can now represent objects stored across multiple packs, so server operators no longer need to consolidate their largest repositories into one pack to benefit from reachability bitmaps. Further background on the design and the steps needed to make this work is available in a previous post on scaling monorepo maintenance.

Default merge strategy changes to ort

For years, Git’s default merge strategy was recursive—a three-way merge that, for “criss-cross” merges without a single merge base, recursively merges the multiple bases together to produce a synthetic merge base. The strategy worked, but Git’s newest strategy, ort (for “ostensibly recursive’s twin”), was built as a full rewrite to correct long-standing performance and correctness issues.

One key design change: ort does not operate on the index the way recursive does. It also caches results from earlier merges, which pays off during rebases that repeat similar merge work. In merges with many renames the authors measured ort outperforming recursive by a factor of 500; for a series of related merges the speedup was over 9,000 times. In Git 2.34, ort is now the default strategy, so upgrades should bring faster and more consistent merges without any configuration changes. Elijah Newren, ort’s creator, documented the strategy’s internals across a six-part series; a summary also appeared in the previous release notes.

SSH keys join the signing toolbox

Git’s object signing has long been tied to GPG, but maintaining PGP keys can be tedious. Git 2.34 adds support for signing objects with SSH keys, capitalizing on a feature introduced in OpenSSH 8.0. If you already have an SSH key, you can use it for signing without any GPG setup.

To enable SSH signing, either set user.signingKey to the specific key you want (you can list available keys with ssh-add -L), or configure Git to auto-select the first available key by setting gpg.format to ssh and gpg.ssh.defaultKeyCommand to ssh-add -L. Once configured, commands like git commit -S, git merge -S, and git tag -s automatically use your SSH key.

Verification of SSH signatures works differently than PGP signatures, using an “allowed signers” file. Details on the new configuration options and verification workflow can be found in the git-config documentation.

Interactive typo recovery

Typing git psuh instead of git push triggers Git’s autocorrect mechanism, which shows a suggestion to rerun the command with the closest match. Previously, you could configure this behavior via help.autoCorrect to either disable the hint (never) or automatically rerun the command, either immediately or after a delay specified in seconds. Git 2.34 adds a prompt option, which causes Git to ask you interactively before executing the suggested command.

Faster fetch and push

Several patch series in this release target performance when communicating with other repositories. One improvement addresses the initial bookkeeping phase of git fetch, where your client determines common objects with the remote. Previously this process loaded commit objects directly from disk; it now reads from the commit-graph when possible, providing a significant speedup in repositories with many refs — in one example with over 2 million refs, fetching a single commit took less than half the time.

A separate series of changes speeds up the actual updating of local references after a fetch, improves fetch negotiation, and skips the connectivity check when the receiving side has already confirmed the objects are connected. Combined, these optimizations deliver substantial improvements to the git fetch command.

$ git psuh
git: 'psuh' is not a git command. See 'git --help'.

The most similar command is
  push

Submodules get renewed attention

Submodules have historically been a less-polished corner of Git, but 2.34 brings multiple enhancements:

  • C rewrite of git submodule. The original git submodule command is a shell script, but this release converts many of its parts into C. This makes the code easier to maintain and eliminates the overhead of spawning numerous subprocesses. Google Summer of Code student Atharva Raykar led the project; final reports from Raykar and fellow GSoC participant ZheNing Hu are available online.
  • Cleaner handling of multi-repository commands. When running operations that need objects from both a superproject and its submodule, Git previously added the submodule as an alternate object store — a working but inelegant hack. Internal changes now parameterize functions by repository, allowing commands like git grep to work with multiple repository instances directly rather than via alternates, resulting in more understandable and less error-prone code.
  • Recursion respects clone settings. Cloning with --recurse-submodules initializes nested repositories, but other commands that can recurse (like git diff) previously would not follow submodules even after such a clone. In 2.34, whether commands recurse after a recursive clone is controlled by a new submodule.stickyRecursiveClone configuration setting, which prevents unintended operations inside submodules while allowing opt-in recursion.

plumbing and polish

  • Faster cat-file --batch-all-objects. The low-level cat-file command supports printing all objects with --batch-all-objects. It previously honored replace refs (refs/replace), which could cause it not to list every object. Dropping replace-ref support also lets the command reuse pack offset information while iterating objects, avoiding a second lookup. In a copy of the Linux kernel, git cat-file --batch-check='%(objectname) %(objecttype)' --batch-all-objects --unordered dropped from 8.1 seconds to 4.3 seconds.
  • Leak detection in tests. Setting GIT_TEST_PASSING_SANITIZE_LEAK=true makes the integration suite fail on memory leaks. A new mode runs only tests explicitly marked leak-free, allowing easy regression detection when building with make SANITIZE=leak. Numerous leaks were fixed to support this.
  • Process ancestry in trace2. The trace2 mechanism reports when child processes start and stop, but not which parent spawned them — a significant gap when diagnosing a slow pack-objects run (is it from a fetch or a repack?). Git 2.34 adds the full ancestry chain to trace2 output.
$ cat trace2.log
21:14:38.170730 common-main.c:48                  version 2.34.0.rc1.14.g88d915a634
21:14:38.170810 common-main.c:49                  start /home/ttaylorr/src/git/git pack-objects git pack-objects --revs --thin --stdout --progress --delta-base-offset
21:14:38.174325 compat/linux/procinfo.c:170       cmd_ancestry sh <- git-upload-pack <- sh <- git <- zsh <- sshd <- systemd
  • systemd timer support for background maintenance. The git maintenance daemon, introduced in 2.31, supported cron on Linux, launchctl on macOS, and schtasks on Windows. Git 2.34 adds support for systemd timers, which isolate each maintenance job and write logs separately.
$ git maintenance start --scheduler=systemd
  • --preserve-merges removed from git rebase. This legacy option for moving complicated branching structures attempted to replay merges but interacted poorly with the interactive machinery, producing counterintuitive results. The --rebase-merges option fixed those issues and has been the recommended approach; the old flag is now dropped.
  • Colorized git log --grep. The --grep option in git log filters commits by message content, but previously did not highlight matches. In 2.34, matching portions of commit messages are now colorized in the output.
h2>Terminal settings restored after editor exits

On Windows (and other systems), running git commit or git rebase can occasionally leave your terminal in a broken state. The cause: Git shares its terminal with any spawned child process, including your $EDITOR. If the editor modifies terminal settings without restoring them on exit, your session remains corrupted. Git 2.34 now saves terminal state before launching your editor and restores it afterward, so even a misbehaving editor cannot leave your terminal unusable.

Beyond the marquee features

The headline changes only tell part of the story. Git 2.34 also brings a long list of smaller fixes and refinements that improve day-to-day workflows.

A few worth noting: the git log and git rev-list commands now support a --filter option that lets you omit blobs from the output, which can speed up large repository operations. The git stash command gained a --staged mode to stash only the changes you’ve already added to the index, leaving working tree modifications intact.

For those who script around Git, the git for-each-ref formatting gained a %(describe) atom, making it easier to include human-readable names in ref listings. Error messages across several commands were also reworked to be more consistent and actionable, particularly in areas such as git checkout, git switch, and git restore.

Performance work in this release includes faster handling of paths with wildcards in git add and friends, plus memory-use reductions in the object store. Bisecting and fetching from partial clones also received several fixes.

There’s much more in the official release notes for 2.34 — or for any prior version in the repository — but the ground covered here should point you toward the areas of greatest impact in this cycle.