Skip to main content

Module merge

Module merge 

Source
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 from HEAD at all (measured in doc/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 from git merge-tree --write-tree, the commit from git commit-tree, and the checkout moves with git 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, which git merge itself uses, fed the description line git merge builds for the commit-ish (branch 'x', tag 'x', remote-tracking branch 'x' or commit '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 merge command lines the wrapper completes in isolation, and those it leaves to native Git. What: The shape of a git merge command 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 a GitRunner. 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 the binary attribute, 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, --edit and similar, which stop as Git would. The continuing step is a command run in this process through run_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 runs prepare_to_commit and commits (merge.c line 1828); with --no-commit or --squash it writes the merge state and prints “Automatic merge went well; stopped before committing as requested” to standard error and exits 0 (merge.c lines 1867 to 1873); the editor opens when --edit is given, or when nothing is given, no -m was given and GIT_MERGE_AUTOEDIT is unset and standard input and output are one terminal (default_edit_option, merge.c lines 1158 to 1180). A cherry-pick or revert commits with the picked commit’s message and edits only when --edit is given, or for a revert on a terminal (should_edit, sequencer.c line 2217); after a conflict --continue runs git commit (continue_single_pick, sequencer.c lines 5354 to 5378) and then picks the rest. A rebase continues with git rebase --continue. git pull runs git merge or git rebase as a child (builtin/pull.c lines 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-tree wrote 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 the union line of .gitattributes applies 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 .gitattributes unless 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, so merge.conflictStyle does 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: A git pull that merges, turned into the fetch it starts with and the git merge FETCH_HEAD it ends with, so the merge half goes through the same isolated merge as a typed git merge. Why: Native Git’s git pull runs 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 held index.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, whether HEAD moved under the fetch, how many heads FETCH_HEAD holds) 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 the similar crate’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.