Git Course 0%

Advanced merging

Expert Git CLI ≈ 10 min

What you will learn

  • What the merge strategies and their options actually change
  • How to take one side of a conflict on purpose, and when that is right
  • rerere, which remembers a resolution you have already made

After this lesson you can

  • I can handle a difficult merge deliberately rather than by trial

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:

Terminal
$ 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:

Terminal
$ 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:

Terminal
$ git checkout --ours docs/faq.md
$ git checkout --theirs docs/faq.md
$ git add docs/faq.md

During 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:

Terminal
$ git config --global rerere.enabled true
Recorded 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:

Output
Recorded resolution for 'docs/faq.md'.

And on the next encounter with that same conflict:

Output
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 main repeatedly.
  • 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:

Terminal
$ 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):

Terminal
$ git subtree add --prefix vendor/lib <url> main --squash
$ git subtree pull --prefix vendor/lib <url> main --squash

Everything 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

Terminal
$ 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.

Common mistakes

  • Confusing -X ours with -s ours. The first prefers your side in conflicts; the second discards theirs entirely.
  • Using --ours on 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-histories when the real problem is the wrong remote.
  • Choosing subtree before trying anything simpler.

Try it yourself

Goal: see rerere remember a resolution.

  1. Enable it: git config rerere.enabled true in a practice repository.
  2. Create two branches that change the same line differently, and merge one into the other.
  3. Resolve the conflict, commit, and note the "Recorded resolution" message.
  4. Undo the merge with git reset --hard HEAD~1, and merge again.
  5. 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.

Check yourself

1. What is the difference between -X ours and -s ours?
2. What does rerere do?
3. During a rebase, --ours refers to:

Key terms

Merge Merge conflict Rebase Fast-forward