Ray's Knowledge Base

How git's octopus merge works, for a tool that must match it

FactVerified 28 Sep 2026Holds anywhere
Fact. A statement and the evidence for it.

Statement#

git merge a b c with several heads runs the octopus strategy, git-merge-octopus.sh. To match git exactly, a reimplementation must follow the script, not a generic three-way merge:

  • For each head, in order: find common=$(git merge-base --all <head> <merge result so far>). When common contains the head, print Already up to date with <name> and skip it.
  • While no real merge has happened yet and common is the current result, fast-forward: print Fast-forwarding to: <name> (git 2.50), run git read-tree -u -m HEAD <head>, and continue.
  • Otherwise print Trying simple merge with <name> and run git read-tree -u -m --aggressive <all merge bases> <result tree> <head>. It passes every merge base at once; it does not build a virtual merge base like ort/recursive. When git write-tree then fails, print Simple merge did not work, trying automatic merge. and run git merge-index -o git-merge-one-file -a.
  • A file-level conflict on any head but the last stops the strategy: Automated merge did not work. and Should not be doing an octopus.. git's merge then restores the tree to HEAD, prints Merge with strategy octopus failed. on stderr with no prefix, and exits 2.
  • A conflict on the last head leaves conflict markers and exits 1 with Automatic merge failed; fix conflicts and then commit the result. on stdout.
  • merge-one-file prints Auto-merging <path> on stdout and ERROR: content conflict in <path> on stderr; merge-index adds fatal: merge program failed on stderr. The conflict markers are labelled with random .merge_file_XXXXXX temp names, so tests must mask them.

On a criss-cross history, a virtual merge base lets a merge succeed where git's octopus fails with a content conflict, so "improving" on git here breaks compatibility.

Evidence#

rgit, 2026-09-28, git 2.50: four histories (clean, conflict on the last head, conflict on a middle head, criss-cross) run with git merge a b c and rgit merge a b c. After rgit's octopus was rebuilt on native read-tree, merge-one-file and write-tree, stdout, stderr, exit codes (0, 1, 2, 2), git status --porcelain, the file content (markers masked) and the new commit's tree matched git in all four. Test octopus_merges_like_gits_script in crates/rgit-cli/tests/sequencer.rs, commit b1a7ca6.