Skip to content

Build2 publishers3 min readPublished

Git 2.56 stops merge-base searches once no new common ancestor can appear

Git 2.56.0 ships a merge-base stopping rule that GitHub measured at around 70 times faster in many cases on one large monorepo. Whether a build fleet sees gains like that depends on its repositories having old side branches merged into long histories.

The Engineer · Build desk

What happened

  • Git 2.56.0 collects 748 non-merge commits since v2.55.0 from 104 contributors, 39 of whom are new to the project.
  • On the Linux kernel, git merge-base --all v4.8 v4.9 fell from 167,441 traversal steps and 0.29 seconds to 3,887 steps and 0.01 seconds with the default v2 commit-graph.
  • A new git add --resolved mode stages only unmerged paths, and it leaves the index untouched if any of them still holds conflict markers.
  • The ort merge backend has been hardened so that it aborts on corrupt trees under appropriate error conditions.
  • A new promisor.acceptFromServerURL configuration variable lets the server side add entries to a client's list of promisor remotes.

Compiled by The EngineerSomething wrong?How this is made

Why it matters

  • decision Repository shape sets the upgrade priority. Fleets working on long histories with old merged side branches stand to gain most, while a 0.28-second saving per kernel-sized query gives a single developer little reason to rush.
  • constraint Merge jobs that ran against a damaged tree can now stop with an error under ort, so a new CI image needs a test pass on real repositories before it reaches every runner.
  • exposure Clients that set promisor.acceptFromServerURL let the server add promisor remotes on their behalf, so that variable belongs in the reviewed part of a build machine's gitconfig.

The old merge-base search had a stopping problem. Git walks backward from both commits and treats any commit reachable from both sides as a candidate [13]. Criss-cross merges can produce several merge bases, none an ancestor of another, so Git must keep walking until it has found all of them [13]. Under the old rule, that walk could keep processing a long tail of stale, already-common history after another merge base had become impossible [13]. Git 2.56 tracks how many queued commits are still reached from only one side. Once one side runs out, no new meeting point can appear, and the implementation adds guards around that rule [14]. "Git can stop while still returning every merge base," wrote Elijah Newren, the GitHub staff engineer who designed merge-ort, Git's default merge strategy since 2.34 [15][21].

I like this change because the output is untouched and only the exit condition moved [14][15]. On the kernel query, traversal steps fell about 43 times and wall time about 29 times [1][4]. At 0.01 seconds, two decimal places cannot resolve much, so the step count is the better guide to what changed [18].

The monorepo numbers come from GitHub's own workloads. One real monorepo case fell from 0.68 seconds to 0.01, about 68 times faster [16][3]. Across two large monorepos, the post reports many cases near 70 times faster in one and an average near 20 times in the other [17]. It says the gain is largest when old side branches have been merged into a much longer history [19]. For the figures to transfer, a repository needs that shape, and the kernel result was measured with Git's default v2 commit-graph [18]. The absolute saving on the kernel query is 0.28 seconds [2]. On a laptop that is hard to notice. The post notes that repository hosts run the same calculation for pull request diffs, mergeability checks and review ranges [20].

The `--resolved` check is narrow on purpose. A pathspec can limit which unmerged paths it considers, and the all-or-nothing marker rule still applies inside that selection [10]. It cannot be combined with `git add -u` or `-A`, and it ignores tracked files that never conflicted [10]. "That makes it a useful safety rail for maintainer workflows where a merge may begin with unrelated local changes already present," Newren wrote [12]. The check is textual. Resolved deletions and binary conflicts have no markers, so Git stages them normally, and the marker scan cannot tell whether a binary conflict was actually resolved [11].

For build images, the release also changes output a pipeline might read. `git log --follow` now handles histories where a tracked path was renamed differently on separate lines [5]. Advice from `git status` for a branch that is behind or has diverged now suggests `git pull <remote> <branch>` [6]. A new `fetch.followRemoteHEAD` variable sets a default for the per-remote `followRemoteHEAD` setting [4]. Git now flags `git push origin/main` as a probable typo and offers a fix, a change that shipped without a benchmark [7]. For a CI fleet built around one large, long-lived repository, I think the upgrade is worth taking after a test run on that repository, since both the faster merge bases and the new ort aborts would show up there first [2][19].

What to watch

  • Independent merge-base timings on repositories without long merged side-branch histories, which would show how far GitHub's 20 to 70 times figures carry.
  • CI merge jobs that start failing under the hardened ort backend, pointing to corrupt trees that earlier releases let through.
  • Whether the experimental git history command, which gained a drop subcommand in 2.56, is declared stable in a later release.
Loading claim ledger
Loading source directory links
Loading share composer
Loading topic controls
Loading related stories