Why GitHub Picked a New Merge Engine

GitHub runs merges and rebases in the background on a massive scale, pre-assembling pull request results before a user even clicks the merge button. Any strategy it adopts has to satisfy three hard requirements: it must be fast enough to sustain millions of daily operations, it must produce results that match user expectations (generally whatever the Git command line does), and it must not require a working directory checkout for scalability and security reasons.

The previous implementation, built on libgit2, did not need a worktree and was fast. But its merge base selection logic occasionally diverged from what a user's local Git produced, meaning the web UI would report a merge conflict where the command line succeeded. That mismatch created a steady stream of support tickets and a correctness gap that grew as Git's default strategy improved.

Adopting merge-ort

Git's merge-ort strategy addressed all three requirements at once. It is significantly faster than both the old default and the optimized libgit2 path, it does not need a working directory, and it has now become Git's default strategy. That last point was decisive: sticking with the old engine would have guaranteed GitHub's behavior fell further behind what users see locally.

The rollout was split into two phases. First, merge-ort was deployed for merge commits. The team used Scientist to run both the legacy and new code paths in production simultaneously, comparing timing and output while customers still received results from the established path. The experiment started on internal repositories, then expanded to a percentage-based rollout across all of production.

Results were dramatic. On the github/github monolith, both average and P99 merge times improved roughly 10x. Across the entire experiment, the P50 also saw a 10x speedup, with the P99 nearly 5x faster.

Chart showing experimental candidate versus control at P50. The candidate implementation fairly consistently stays below 0.1 seconds.

Chart showing experimental candidate versus control at P99. The candidate implementation follows the same spiky pattern as the control, but its peaks are much lower.

Dashboard widgets showing P50 average times for experimental candidate versus control. The control averages 71.07 milliseconds while the candidate averages 7.74 milliseconds.

Dashboard widgets showing P99 average times for experimental candidate versus control. The control averages 1.63 seconds while the candidate averages 329.82 milliseconds.

Extending to Rebases

Rebases presented a second major opportunity. GitHub performs them for customers who choose rebase workflows on pull requests, as well as for internal test rebases. This phase was powered by git-replay, a new Git subcommand written by Elijah Newren, the original author of merge-ort. It enabled rebases with the merge-ort engine while still avoiding a worktree.

The deployment process mirrored the merge rollout but added several layers of validation:

  1. Backport git-replay into GitHub's Git fork, since the experimental environment ran Git 2.39 without the feature.
  2. Run the existing test suite to catch discrepancies between old and new implementations.
  3. Automate test rebases of all open pull requests in github/github to surface bugs.
  4. Launch a Scientist experiment comparing libgit2-powered rebases with the new engine, monitoring for unexpected behavioral mismatches.
  5. Measure gains, verify correctness, and fix bugs iteratively.

Resource usage during the experiment illustrates the scale of the improvement. Over 730k experiment runs, computers spent 2.56 hours performing rebases with libgit2 but under 10 minutes with merge-ort — and that was at only 0.5% of traffic. Extrapolated to full production, the same rebase work would have taken roughly 33 hours with merge-ort versus 512 hours with libgit2.

Another way to think of this is in terms of resource usage. We ran the experiment over 730k times. In that interval, our computers spent 2.56 hours performing rebases with libgit2, but under 10 minutes doing the same work with merge-ort. And this was running the experiment for 0.5% of actors. Extrapolating those numbers out to 100%, if we had done all rebases during that interval with merge-ort, it would have taken us 2,000 minutes, or about 33 hours. That same work done with libgit2 would have taken 512 hours!

Remaining Work

Merges and rebases cover the most common paths, but GitHub sees further opportunities to apply merge-ort. Squashing and reverting are next on the roadmap, along with exploring new product features the engine's performance could unlock. The work also continues to depend on contributions from the broader Git open source community, particularly Elijah Newren's ongoing development of merge-ort itself.