Expand description
Merges: the word-level second pass, isolated merges and pull handling. What: Merges: the word-level second pass, isolated merges and pull handling. Why: Merge code shares fixtures and a journal, so it is kept together.
In TS you’d write (pseudocode):
// merge/index.ts: one `export * from './file'` per module below.Modules§
- merge_
isolation - A true merge completed in isolation while the index holds staged changes.
What:
A true
git merge <branch>completed in isolation when the index holds staged changes that make native Git refuse it: the merge is computed from objects, committed, and applied with Git’s own two-way tree update, which keeps every entry and every file the merge does not change. Why: Several sessions share one checkout. Native Git refuses every merge that creates a commit while the index differs fromHEADat all (measured indoc/planning/cli-git-merge-with-staged-index.md), although the merge may touch none of the staged paths. A fast-forward is not refused for unrelated paths, because it is a two-way update that keeps entries it does not write; a merge commit whose first parent is the current commit is exactly such an update. So the merge tree comes fromgit merge-tree --write-tree, the commit fromgit commit-tree, and the checkout moves withgit merge --ff-only <commit>, which refuses when the branch is no longer where the merge was computed, after a check that no changed path is staged or modified by someone else. An intent record, written before Git starts, lets recovery finish or discard a merge whose process died. - merge_
isolation_ changes - The raw differences of an isolated merge and the worktree state of each changed path. What: The raw tree differences between the two commits of an isolated merge, and the state of a worktree path against them. Why: Recovery of a merge killed while Git was updating the worktree must tell, for each path the merge changes, whether the worktree still holds the old content, already holds the new content, or holds something else that belongs to someone else and must not be overwritten.
- merge_
isolation_ journal - The durable intent record of an isolated merge. What: The durable intent record of an isolated merge: the commit it created, the branch value it expects, and the process that runs it. Why: Between the moment Git starts updating the worktree and the index for the merge commit and the moment the branch moves, a killed process leaves a worktree and an index that agree with the merge while the branch does not. The record, written and synced before Git starts, lets the next command decide whether the merge never started (discard it) or must be completed. Records are created once and never rewritten, like the commit transaction’s own state files.
- merge_
isolation_ message - The commit message of an isolated merge.
What:
The text of an isolated merge’s commit message and the name Git gives the merged commit in it.
Why:
The merge commit must read exactly as native Git would write it: the default subject comes from
git fmt-merge-msg, whichgit mergeitself uses, fed the description linegit mergebuilds for the commit-ish (branch 'x',tag 'x',remote-tracking branch 'x'orcommit 'x'), and any message is cleaned the way a commit that opens no editor cleans it. A commit-ish whose description cannot be built the same way is not guessed: the caller forwards the command. - merge_
isolation_ moved - The settling of an isolated merge whose branch moved while it was prepared or applied. What: The settling of an isolated merge whose branch moved while the merge was prepared or applied: what the merge had begun to install is put back to the moved head’s content, and the record goes, so the merge can be computed again on the moved head. Why: Git’s fast-forward installs the checkout of the paths the merge changes and then moves the branch from the commit the merge was computed on. When another session (or the auto-push’s reconcile merge) moved the branch first, the move is refused: nothing was installed if Git refused before it began, and the merge’s paths are installed beside a branch that does not hold them if it lost the race in the instant between. Both end with the checkout showing what the moved head holds for those paths, never a half-applied merge and never a record that fails every later command. Only paths that hold the old, the merged or the moved head’s content are written; any other content belongs to someone else and stops the settling, naming the paths, with the record kept.
- merge_
isolation_ recovery - Recovery of an isolated merge whose process died. What: Recovery of an isolated merge whose process died: discard it when it never touched the worktree or the index, complete it when Git had started to update them. Why: The merge commit exists before the branch moves, and Git updates the worktree and the index of the paths the merge changes before it moves the branch. A process killed in between leaves files and index entries of the merge beside a branch that still names the old commit, which the next commit would then revert. Completing the merge from its record finishes exactly what Git had started, for exactly the paths the merge changes, and leaves every other staged entry and every unstaged change as it was. A path that holds neither its old nor its new content belongs to someone else, so recovery stops and keeps the record instead of overwriting it.
- merge_
isolation_ report - The refusal findings of an isolated merge. What: The refusals of an isolated merge, as the finding events a caller reads, each naming the paths involved and every valid way forward. Why: A merge that cannot proceed changes nothing, and the person (or the agent) who ran it must learn which files stand in the way and what to do next without reading the wrapper’s source.
- merge_
options - The
git mergecommand lines the wrapper completes in isolation, and those it leaves to native Git. What: The shape of agit mergecommand line that the wrapper can complete in isolation, or the reason it cannot. Why: The isolated merge reproduces what native Git writes only for options whose effect is known exactly: the message, the fast-forward choice, signing and the unrelated-histories switch. Any other option (a strategy,--squash,--no-commit,--ff-only,--edit, an abbreviation Git would accept) is not guessed: the command is forwarded to native Git unchanged, with the refusal it may give. - merge_
second_ pass - The conflicted files a merge left, which of them the word pass may re-merge, and the lines that list each merged part.
What:
The word-level second pass over the files a Git merge left conflicted: which conflicted files may be re-merged, their
re-merge by
word_merge, the objects and the tree that carry the result, and the words that list every merged part on standard error and in the journal. Why: The owner decided on 2026-11-03 (doc/planning/cli-git-merge-auto-resolution.md) that the merges a person runs and the wrapper’s own unattended merges get a second pass that accepts a file only when every part resolves and shows each part. Four callers need the same answer from the same stage blobs: the forwarded merge, rebase, cherry-pick and revert (merge_second_pass_worktree.rs), the isolated merge, the auto-push reconcile merge and the commit replay merge, which work from objects (merge_second_pass_tree.rs). They differ in how Git is started, so they hand in aGitRunner. Only a plain file whose three stages exist, with one mode on all three, that is text, small, and not claimed by a merge driver or thebinaryattribute, is re-merged; everything else is left to the person exactly as Git left it. - merge_
second_ pass_ finish - The finish of a stopped operation, like a clean run, after the second pass resolved every conflicted file.
What:
The finish after the word-level second pass resolved every conflicted file of a merge, rebase, cherry-pick, revert or pull:
the operation ends the way Git ends it after a clean run, through the wrapper’s own command path.
Why:
The owner decided on 2026-11-03 (“Finish like a clean merge”): when every conflicted file resolves, make the merge
commit with Git’s prepared message, or continue the rebase or cherry-pick, and honour
--no-commit,--squash,--editand similar, which stop as Git would. The continuing step is a command run in this process throughrun_process, so the commit policies and hooks apply to it and a later pick of a rebase that conflicts gets the second pass again. What a clean run does, from Git 2.56.0 (builtin/merge.c,sequencer.c,builtin/pull.c): a merge that is clean and wants a commit runsprepare_to_commitand commits (merge.cline 1828); with--no-commitor--squashit writes the merge state and prints “Automatic merge went well; stopped before committing as requested” to standard error and exits 0 (merge.clines 1867 to 1873); the editor opens when--editis given, or when nothing is given, no-mwas given andGIT_MERGE_AUTOEDITis unset and standard input and output are one terminal (default_edit_option,merge.clines 1158 to 1180). A cherry-pick or revert commits with the picked commit’s message and edits only when--editis given, or for a revert on a terminal (should_edit,sequencer.cline 2217); after a conflict--continuerunsgit commit(continue_single_pick,sequencer.clines 5354 to 5378) and then picks the rest. A rebase continues withgit rebase --continue.git pullrunsgit mergeorgit rebaseas a child (builtin/pull.clines 525 and 750), so the state files tell which of the two stopped. The options the person typed are read only by exact spelling; any option this reader does not know leaves the operation stopped, with the files staged, as Git left it. - merge_
second_ pass_ tree - The word-level second pass for merges computed from objects, and the attribute tree those merges read.
What:
The word-level second pass for a merge computed from objects: given the tree
git merge-treewrote and the stages of its conflicted paths, the tree the merge would have if every conflicted file resolves by word, or nothing. It also names the attribute tree those merges read, so theunionline of.gitattributesapplies without a worktree. Why: The isolated merge, the auto-push reconcile merge and the commit replay merge never touch a worktree. They publish or commit a merge only when it is whole, so a tree is returned only when every conflicted path resolved; one path left over keeps the conflict as Git reported it, and nothing is written for it except the unreferenced objects. Without a worktree Git finds no.gitattributesunless it is told where to read them (GIT_ATTR_SOURCE,git --attr-source,attr.tree;doc/planning/cli-git-merge-auto-resolution.md, “Does git merge-tree honour a merge driver”). The tree of the first side’s tip is the one the person’s own checkout of that branch reads. - merge_
second_ pass_ worktree - The word-level second pass after a forwarded merge, rebase, cherry-pick or revert that Git stopped with a conflict.
What:
The word-level second pass after a forwarded merge, rebase, cherry-pick or revert that Git stopped with a conflict:
each conflict that command created is re-merged from the index stages, and a file whose every part resolves is
written to the worktree, staged as resolved, and listed on standard error and in the journal.
Why:
The owner decided on 2026-11-03 (
doc/planning/cli-git-merge-auto-resolution.md) that a person’s merge gets this pass. The pass touches only the paths that were not conflicted before the command ran, so a file the person is resolving by hand (it is still unmerged when a later command fails on it) is never replaced, and it reads the stage blobs, never the marked-up worktree file, somerge.conflictStyledoes not matter. Git’s exit status is kept: the operation is not finished, the person finishes it (git commit,git rebase --continue, …), and a script that tests the status still stops. A file that does not resolve, and a file whose staging fails, stays exactly as Git left it. - pull_
isolation - A pull that merges, read as its fetch and its merge of
FETCH_HEAD. What: Agit pullthat merges, turned into the fetch it starts with and thegit merge FETCH_HEADit ends with, so the merge half goes through the same isolated merge as a typedgit merge. Why: Native Git’sgit pullruns its merge as a child of Git (builtin/pull.c,run_merge,cmd.git_cmd = 1), so the wrapper never saw it and the merge kept native Git’s failures: a staged index, a heldindex.lock, a late ref update. The owner’s rules are “Auto merging should never fail.” and “Manual merge should never fail too.”; the pull’s own decisions (which options, whether it rebases, whetherHEADmoved under the fetch, how many headsFETCH_HEADholds) are mirrored here only where they can be mirrored exactly, and every other pull is left to native Git. - word_
merge - The three-way merge by line and by word that resolves a conflict Git’s line merge reports.
What:
A three-way merge of text by line, and by word for the line regions that both sides changed, in process.
A file is merged only when every such region resolves; the regions that needed the word pass are reported.
Why:
Git’s line merge calls two changes that touch (one ends where the other begins) a conflict, so an edit of a line plus
lines inserted right after it stops a merge that nobody needs to judge. The owner chose, on 2026-11-03, a second pass
in the way Jujutsu merges with
merge.hunk-level = "word": re-merge the conflicted regions word by word, keep the result only when every region resolves, and show each region it merged. The diff is thesimilarcrate’s Myers algorithm; the controls keep the results equal to Jujutsu 0.46.0’s on the stored corpus (src/native/merge/word_merge_corpus/,doc/planning/cli-git-merge-auto-resolution.md). The pass can produce text neither side wrote (two edits of different arguments of one call); it never drops a byte that either side added or kept.