Advanced merging
Expert Git CLI
Why this matters
Most merges need nothing beyond Section 11. The remainder, a long-running branch, a generated file that conflicts every time, a repeated rebase, are where these tools earn a page.
Strategies
The default is ort, which replaced recursive and handles the ordinary case of two branches from a common ancestor. Two options are worth knowing:
$ git merge -X ours <branch>
$ git merge -X theirs <branch>-X is a strategy option, not a strategy. It says: merge normally, and where a conflict would otherwise stop us, prefer this side. Everything that does not conflict is still merged from both.
The dangerous confusion is with -s ours:
$ git merge -s ours <branch>That is a strategy, and it takes your side entirely, discarding every change from the other branch while still recording a merge. It has legitimate uses, such as marking a branch as merged when its work has been superseded, and it is otherwise a way to lose work quietly.
| Form | Effect |
|---|---|
-X ours |
Merge both; prefer our side only in conflicts |
-X theirs |
Merge both; prefer their side only in conflicts |
-s ours |
Take our side entirely; ignore theirs, but record a merge |
Taking one side per file
Mid-conflict, when a whole file should come from one side:
$ git checkout --ours docs/faq.md
$ git checkout --theirs docs/faq.md
$ git add docs/faq.mdDuring a merge, "ours" is the branch you are on. During a rebase the meaning is reversed, because your commits are being replayed onto theirs (lesson 11.5). When in doubt, git status names the branches.
This is right for a generated file, a lock file, or anything where one side is authoritative by construction. It is wrong for prose, where the resolution is usually neither side.
rerere: remember the resolution
Reuse recorded resolution. Turn it on once:
$ git config --global rerere.enabled trueRecorded preimage for 'docs/faq.md'Then Git records how you resolve each conflict, and applies the same resolution automatically if it meets the same conflict again:
Recorded resolution for 'docs/faq.md'.And on the next encounter with that same conflict:
CONFLICT (content): Merge conflict in docs/faq.md
Resolved 'docs/faq.md' using previous resolution.
Automatic merge failed; fix conflicts and then commit the result.Git applied your previous resolution and still stops so you can check it, which is the right balance.
Where this pays off:
- A long-lived branch merged from
mainrepeatedly. - A rebase you abort and retry, meeting the same conflicts each time.
- A conflict you resolved on a branch that was later rebased.
git rerere status and git rerere forget <path> inspect and clear it. The recorded resolutions are local, in .git/rr-cache, so they help you and nobody else.
Merging unrelated histories
Combining two projects that never shared a commit is refused by default:
$ git merge --allow-unrelated-histories <branch>The flag exists because the usual cause of that message is a mistake, such as pulling from the wrong repository. When it is deliberate, the flag is the whole answer.
Subtree merges
Occasionally a whole project needs to live inside another, with its history, and submodules are unwanted (lesson 13.10):
$ git subtree add --prefix vendor/lib <url> main --squash
$ git subtree pull --prefix vendor/lib <url> main --squashEverything is in one repository, so a clone gets the whole thing with no extra step, which is the advantage over submodules. The cost is a more complicated history and commands people rarely remember. Use it when a submodule has already proved too fiddly, not first.
How to do it
$ git merge -X ours <branch>
$ git checkout --theirs <file> && git add <file>
$ git config --global rerere.enabled true
$ git rerere status
$ git merge --allow-unrelated-histories <branch>git status during any conflict names which branch is which, which is worth reading before choosing a side.
The merge editor covers per-conflict choices; strategy options and rerere are terminal-only (lesson 15.7).
The merge dialog's Accept Yours and Accept Theirs per file are checkout --ours and --theirs in effect (lesson 16.7). rerere works if enabled in configuration, silently.
Nothing here is exposed on the platform; a merge request with conflicts tells you to resolve locally, and these are the tools for the hard cases (lesson 11.4).
The same.
Common mistakes
- Confusing
-X ourswith-s ours. The first prefers your side in conflicts; the second discards theirs entirely. - Using
--ourson prose, which silently drops the other author's wording. - Enabling rerere and then not checking what it applied. It stops for a reason.
- Reaching for
--allow-unrelated-historieswhen the real problem is the wrong remote. - Choosing subtree before trying anything simpler.
Try it yourself
Goal: see rerere remember a resolution.
- Enable it:
git config rerere.enabled truein a practice repository. - Create two branches that change the same line differently, and merge one into the other.
- Resolve the conflict, commit, and note the "Recorded resolution" message.
- Undo the merge with
git reset --hard HEAD~1, and merge again. - Read the output: Git resolves the file itself and still stops for you to confirm.
Expected result: the same conflict resolved twice, the second time by Git, using what you did the first time.
Show solution
Step 5's behaviour is the design worth appreciating: rerere never commits for you. It reapplies the resolution and leaves the merge unfinished, so a resolution that was right last week but wrong today is caught by the person, not by the tool.