Git 2.36 ships with a remerge diff view and finer-grained fsync controls
The latest Git release, version 2.36, is out with contributions from more than 96 developers, 26 of whom are new to the project. Here are the changes worth knowing about.
See how merge conflicts were resolved with --remerge-diff
Building on the ort merge engine introduced in Git 2.33, this release adds a --remerge-diff option to git show. To understand what it does, it helps to know how git show normally handles merge commits.
Pass a conflicted merge commit to git show and you get output from git diff-tree -cc, which displays simultaneous diffs between each parent and the final merged state. That output has two columns of diff markers, one for each side of the merge, and it can be hard to follow.

In the example above, the conflict comes from two unrelated changes: one side of the merge added an argument to a dwim_ref() call, while the other updated a comment to reflect a rename from sha1 to oid. The different marker columns correspond to different parent sides of the merge, and reading the output requires mentally combining these streams.
With Git 2.36, the --remerge-diff option takes a simpler approach: instead of comparing the merged result to each parent, it first reconstructs the conflicted state and then diffs that conflicted version against the final resolution.

That output, from the same merge commit as above, shows diff3-style conflict markers being removed as the resolution is applied. This makes it clear which sides of the conflict were kept, changed, or dropped. The reconstruction is fast enough to be practical thanks to ort, which can rebuild all conflicted merges in the Linux kernel repository in about three seconds—roughly ten times faster than the older approach via diff-tree -cc, which takes over 30 seconds.
You can try it with git show --remerge-diff on merge commits in your own history.
New configuration options for fsync behavior
Git writes numerous file types into .git—objects, references, reflogs, packfiles, and configuration. When you make commits, update branches, or repack, those writes are not necessarily persisted to disk immediately. Instead, they land in memory caches and are flushed periodically, which is far faster than writing directly to storage.
Previously, Git’s configuration for controlling this behavior was limited to core.fsyncObjectFiles, which determines whether loose object files get an explicit fsync() call when created. Other operations like packfile writing or commit-graph updates had built-in (but not configurable) fsync calls.
Git 2.36 replaces that limited setup with two new configuration variables: core.fsync and core.fsyncMethod. The first takes a comma-separated list of components to flush after writing. Options include pack for files in $GIT_DIR/objects/pack, loose-object for individual objects, and reference for refs under $GIT_DIR/refs. There are also aggregate values such as objects (loose objects plus packs), derived-metadata, committed, and all.
The second variable, core.fsyncMethod, controls how the flush is performed. Setting it to fsync issues an actual fsync() call (or a macOS-specific fcntl()), while writeout-only schedules data to be written out without guaranteeing that directory metadata is updated in the process.
These defaults will be fine for most users, but operators running many repositories on hardware that could lose power abruptly now have more precise control over data durability.
Tightened ownership checks for repositories
Git 2.35.2 introduced a security change: Git refuses to run commands in repositories owned by a different user than the current one, blocking potential execution of commands the repository owner had configured. The safe.directory configuration option can whitelist trusted repositories owned by others.
With Git 2.36, the safe.directory value * is now interpreted as a wildcard, marking all repositories as safe regardless of owner. Setting that in your global config opts you out of the ownership check entirely, which may be useful in some environments. If you haven’t yet upgraded, the safest path remains installing the latest Git release; if that isn’t possible, the previous security advisory contains mitigation steps.
Smaller Updates Worth Knowing
Beyond the headline features, Git 2.36 brings a number of useful refinements for working with repository internals, scripting around objects, and recovering from odd states.
More Flexible Object Inspection with cat-file
For those who work directly with Git objects, git cat-file is a familiar tool. Its --batch mode prints the contents of objects supplied via stdin, while --batch-check reports metadata like type and size. Previously, dynamically switching between these two behaviors required running two separate cat-file processes.
Git 2.36 introduces --batch-command, which lets a single invocation handle requests for either type of output. You send lines like contents <object> (equivalent to --batch) or info <object> (equivalent to --batch-check). For server operators running long-lived cat-file processes, the new mode also accepts a flush command to explicitly clear the output buffer.
Richer Output Control for ls-tree
git ls-tree has long been the go-to for listing tree contents. Its output customization was previously limited to options like --name-only, --full-name, and --abbrev. Git 2.36 adds a complementary --object-only option and, more significantly, a --format option that lets you select any combination and ordering of an entry's mode, type, name, and size.
$ git ls-tree HEAD -- builtin/
100644 blob 3ffb86a43384f21cad4fdcc0d8549e37dba12227 builtin/add.c
100644 blob 0f4111bafa0b0810ae29903509a0af74073013ff builtin/am.c
100644 blob 58ff977a2314e2878ee0c7d3bcd9874b71bfdeef builtin/annotate.c
100644 blob 3f099b960565ff2944209ba514ea7274dad852f5 builtin/apply.c
100644 blob 7176b041b6d85b5760c91f94fcdde551a38d147f builtin/archive.c
[...]
This makes tasks like surveying blob sizes much more convenient. Previously, you'd need to chain awk over the full output, or parse the --long format. Now you can retrieve just the sizes directly:
$ dist () {
ruby -lne 'print 10 ** (Math.log10($_.to_i).ceil)' | sort -n | uniq -c
}
$ git ls-tree --format='%(objectsize)' HEAD:builtin/ | dist
8 1000
59 10000
53 100000
2 1000000
Smarter Error Handling in git bisect run
git bisect run automates bisection by executing a supplied script whose exit status classifies each commit as good or bad. A common setup is a build check:
$ git bisect start <bad> <good>
$ git bisect run make
This works great for simple cases. But for more complex tests, you might write a standalone script and pass it to git bisect:
$ vi test.sh
# type type type
$ git bisect run test.sh
A frequent mistake is forgetting to make that script executable. In earlier versions, git bisect would misinterpret the resulting "permission denied" errors as a test failure and continue searching, blaming every commit incorrectly. Git 2.36 now detects this situation and halts the bisection early instead of producing a bogus result.
Escaping a Corrupt Repository with --refetch
Normal git fetch operations rely on a negotiation process, where the server sends only the objects between what you have and what you're asking for. This assumes your local objects are trustworthy. If your objects directory is corrupt, the server's omission of objects you already possess can prevent you from repairing the damage without a full re-clone.
Git 2.36 adds a --refetch flag to git fetch. Using it instructs the command to ignore what you have locally and download all objects from the remote, providing a recovery path for certain types of corruption.
Expanding Sparse Index Coverage
The work to make more commands compatible with the sparse index continues in this release. Four more plumbing commands now support it: git clean, git checkout-index, git update-index, and git read-tree. This groundwork is intended to eventually support a sparse index-aware git stash.
As a bonus, the git sparse-checkout command gained command-line completion support in Git's contrib directory. Most day-to-day commands like git status, git commit, and git checkout already work with sparse indexes from prior releases.
Partial Clone Fixes and Partial Bundles
If you've used partial clones with git clone --recurse-submodules, the --filter specification was only applied to the top-level repository; all submodule objects were downloaded in full. Git 2.36 fixes this so the filter is now applied recursively to submodules.
This release also introduces partial bundles. A bundle contains a packfile plus a list of references, suitable for sharing repository state in a single file. Previously, you couldn't create a filtered bundle that would work with a partial clone. Now you can, though you can't yet initialize a fresh clone from one. You can, however, use it to fetch objects into a bare repository:
$ git bundle create --filter=blob:none ../partial.bundle v2.36.0
$ cd ..
$ git init --bare example.repo
$ git fetch --filter=blob:none ../partial.bundle 'refs/tags/*:refs/tags/*'
[ ... ]
From ../example.bundle
* [new tag] v2.36.0 -> v2.36.0
Multi-Pack Bitmap Corruption Fix
A bug affecting the multi-pack reachability bitmaps has been fixed. Users of this feature will find several files in their .git/objects/pack directory:
$ ls .git/objects/pack/multi-pack-index*
.git/objects/pack/multi-pack-index
.git/objects/pack/multi-pack-index-33cd13fb5d4166389dbbd51cabdb04b9df882582.bitmap
.git/objects/pack/multi-pack-index-33cd13fb5d4166389dbbd51cabdb04b9df882582.rev
In order, these are the multi-pack index (MIDX), its reachability bitmap, and a reverse-index that maps bits to objects. All are linked by the MIDX's checksum. In certain scenarios—like changing the object order without changing the tracked set—the .rev file could fall out of sync with the MIDX and bitmap, causing incorrect results.
If your .rev file is significantly older than the MIDX and .bitmap, you may have been affected. The fix involves deleting and regenerating the bitmaps. To prevent recurrence, the .rev contents are now incorporated into the MIDX itself, ensuring its checksum changes whenever the object order does.
Performance and integrity work under the hood
Two other areas received substantial attention in Git 2.36: the speed of git fetch and the reliability of reachability bitmaps.
Faster fetch negotiation
The negotiation phase of a fetch—where client and server figure out which objects the client already has—got a serious optimization for the common case. Previously, the server would walk through the entire set of refs advertised by the client, even when the client was only interested in a handful of them. In a repository with thousands of refs, that walk made negotiation needlessly slow.
The new logic identifies which advertised refs the client actually cares about and skips the rest during the initial negotiation walk. For large repositories this can cut the time spent on the negotiation round-trips noticeably. The implementation is also a stepping stone: the same mechanism is reusable for other operations that need to reason about a subset of a remote's refs, such as git push with --negotiate-only.
Safer bitmaps for clone and fetch performance
Bitmaps are the on-disk acceleration structure that lets Git answer "which objects do I have that you don't?" without walking the whole object graph. In 2.36 the bitmapping code gained a new test mode that writes a second, test-only bitmap and cross-checks it against the real one. This is meant to catch bitmap bugs before they ship, but it also changed how bitmap traversal works internally, making the code more composable and easier to reason about.
The release also fixed few correctness problems around git fetch using bitmaps in repositories that also make use of the multi-pack-index. When a MIDX is in play and bitmaps are written, the bitmap must reference the correct packfiles. There was a window where a fetch could write a new pack, then update the MIDX, and the existing bitmap would get out of sync—reporting that objects from the new fetch were reachable when they weren't. That could lead to `git clone` or `git fetch` against a corrupt bitmap, returning incorrect object sets (with missing or extra objects) instead of failing cleanly.
The fix makes sure that after modifying packs or the MIDX, Git invalidates any loaded bitmap state so the next operation re-reads the correct data from disk. If you suspect bitmaps in an existing repository have been corrupted by this bug, rebuilding is straightforward: remove the MIDX files with rm -f .git/objects/pack/multi-pack-index* and then regenerate everything with git repack -d --write-midx --write-bitmap-index.
One footnote to the broader bitmap and MIDX work: the new traversal code actually touches on several of the internal assumptions about what "reachable" means. In a normal, fully-cloned repository, every object that is reachable from any ref should exist locally—the only exceptions are exotic setups like shallow clones, partial clones from an promisor remote, or grafts. Where that closure fails in a regular repo, the only explanation is corruption, and the new traversal logic will more readily detect that case rather than silently producing a wrong answer.
You can also manually validate whether your bitmaps are intact with git rev-list --test-bitmap HEAD.



