Git Merge, Rebase & Squash — Part 5: Conflicts, ours/theirs, and Merge Strategies

On this page
Abstract
Version control systems typically automate branch integration, but concurrent modifications to identical lines demand manual conflict resolution. Understanding these underlying mechanics prevents unintended code loss. When integrating branches, a standard merge halts for resolution just once, whereas a rebase replays individual commits and may repeatedly surface the same discrepancy. Resolving these clashes involves selecting file versions via the ours or theirs commands; however, a rebase inverts their meanings, treating the base branch as ours. Furthermore, users must distinguish between merge methods, like squash or fast-forward, and underlying merge strategies. Git defaults to the ort strategy for two-head merges, while octopus combines multiple non-conflicting branches, and subtree integrates differing repository roots. Crucially, operators must not conflate the -s ours strategy, which discards incoming content entirely, with the -X ours or -X theirs options, which act as automated tie-breakers that risk silently dropping code. This article outlines these workflows to ensure developers safely arbitrate integrations.

Parts 1 to 4 assumed the happy path — that Git could combine your work automatically. This part is about what happens when it cannot. When two branches change the same lines, Git stops and hands the decision back to you, and the way a conflict appears (and how you resolve it) is different for merge and for rebase. We then open the machinery Git uses to combine branches: the difference between a merge method (what you click in a UI) and a merge strategy (the algorithm underneath), plus the handful of strategies and strategy options that occasionally matter — ort, octopus, -s ours, -X ours/-X theirs, and subtree.
By the end you will be able to resolve a conflict during both merge and rebase, use ours/theirs without being fooled by their reversed meaning under rebase, and tell apart two dangerous-looking siblings that behave very differently: -s ours and -X ours.
Conflicts during a merge
Suppose both branches changed the same line. You start an ordinary merge:
git switch main
git merge feature/login
Git cannot decide which version should win, so it reports a conflict:
CONFLICT (content): Merge conflict in app.js
The reason is visible in the graph — main and feature/login both edited the same line after diverging at C:
Inside the file, Git marks the clash with conflict markers:
<<<<<<< HEAD
const title = "Main version";
=======
const title = "Feature version";
>>>>>>> feature/login
Edit the file down to the single version you actually want:
const title = "Final version";
Then stage the resolved file and complete the merge:
git add app.js
git commit
On newer Git versions you can finish with:
git merge --continue
Aborting a merge
If you would rather back out and return to where you started:
git merge --abort
Conflicts during a rebase
Now start a rebase from the feature branch:
git switch feature/login
git rebase main
When a conflict stops the rebase, inspect the state:
git status
Fix the file, then continue:
git add app.js
git rebase --continue
Skipping the current commit
git rebase --skip
Use this only when you are sure the commit being replayed is no longer needed.
Aborting the whole rebase
git rebase --abort
This returns you to exactly the state you were in before the rebase began.
Why a rebase can conflict several times
This is the key mental difference. A rebase replays your commits one at a time, so the same underlying disagreement can surface at every replayed commit, whereas a merge usually asks you to resolve it just once:
So a long-running feature branch that conflicts with heavy changes on main may cost you several rounds of the same fix under rebase — something to weigh when choosing between the two.
ours and theirs (and why rebase flips them)
When you hit a conflict you can take one whole side of a file instead of hand-editing it. During a merge:
git checkout --ours app.js
means "use the file from the current branch", while:
git checkout --theirs app.js
means "use the file from the branch being merged in". Newer Git offers the same thing through restore:
git restore --ours app.js
git restore --theirs app.js
Then stage and continue as usual:
git add app.js
git merge --continue
The rebase inversion — a safety warning
During a rebase,
oursandtheirsappear reversed from the intuition you built during a merge.
Because a rebase replays your commits on top of the base branch, Git treats the base as the side it is building onto:
oursusually refers to the base that is being rebuilt (the branch you are rebasing onto).theirsusually refers to the commit being replayed (your own feature commit).
So always open the actual file and confirm its contents. Never pick a side by its name alone.
Merge strategy is not the same as merge method
Merge method
The choice most people make in a UI or at the top level:
Fast-forward
Merge commit
Squash merge
Rebase and merge
Merge strategy
The internal algorithm Git uses to compute the combination. You select it with:
git merge -s <strategy>
The method is what you want the history to look like; the strategy is how Git computes the merged tree. They are separate choices, and most of the time you set the method and let Git pick the strategy.
The ort strategy (the modern default)
This is the main strategy for merging two branches:
git merge -s ort feature/login
You normally never need to type it — Git selects it for you automatically. ort stands for the idea "Ostensibly Recursive's Twin". It is well suited to:
- two-head merges (one branch into another);
- rename detection;
- ordinary, everyday merges.
octopus: merging more than two branches
The octopus strategy merges more than two branches at once:
git merge feature-a feature-b feature-c
It is typically used to combine branches that do not conflict. If any conflict needs manual resolution, octopus refuses the merge and you must fall back to merging the branches one at a time instead.
-s ours: keep this branch, record the merge
git merge -s ours obsolete-branch
This strategy:
- creates a merge commit;
- records that the other branch was merged;
- but discards the other branch's content entirely;
- keeping only the content of the current branch.
It must not be confused with:
git merge -X ours feature
These two are genuinely different things:
| Aspect | -s ours (strategy) |
-X ours (strategy option) |
|---|---|---|
| What gets merged | Nothing from the other branch — its content is discarded completely | Everything merges normally |
| Effect on conflicts | Not applicable — no real content merge happens | Only conflicting hunks are resolved toward the current branch |
| Resulting tree | Identical to the current branch, but a merge commit records the other branch as merged | A normally merged tree, biased to your side wherever hunks collide |
| Typical use | Marking an obsolete branch as merged without taking any of its changes | Auto-resolving conflicts toward your side during a real merge |
-X ours and -X theirs: conflict tie-breakers
git merge -X ours feature/login
On each conflicting hunk, prefer the current side.
git merge -X theirs feature/login
On each conflicting hunk, prefer the incoming side. Crucially, the parts that do not conflict are still merged normally.
Do not use
-X oursor-X theirswithout reviewing the result — they can silently drop important code. Because Git resolves every conflicting hunk in one direction without asking, real changes on the losing side can vanish with no warning.
subtree: merging repositories with different roots
The subtree strategy is used to combine a repository or directory whose root structure differs from your own. Conceptually:
main repository:
src/
docs/
external repository:
library files at the repository root
Git can adjust the paths during a subtree merge so the incoming tree lands in the right subdirectory. In practice, many teams now use the dedicated git subtree command, or a package manager, rather than managing this strategy by hand.
Key takeaways
- A merge usually asks you to resolve a conflict once; a rebase replays commits one at a time, so the same conflict can reappear at every commit — drive it with
--continue,--skip, and--abort. ours/theirsmean "current branch"/"incoming branch" during a merge, but rebase reverses them, so always inspect the actual file before choosing a side.- A merge method (fast-forward, merge commit, squash, rebase-and-merge) is what you pick; a merge strategy (
-s) is the algorithm underneath —ortis the modern default and rarely needs to be named. -s oursthrows away the other branch's changes entirely while recording it as merged;-X oursmerges normally and only breaks conflicting hunks toward your side — very different tools.-X ours/-X theirscan silently drop code; never use them without reviewing the merged result.