Git’s merge strategies are more like negotiation tactics than simple commands.

Let’s see what happens when we actually merge. Imagine you have a main branch and a feature branch.

# Start with a clean main branch
git checkout main
git status # Should be clean

# Create a feature branch and make some changes
git checkout -b feature
echo "Feature change 1" > feature.txt
git add feature.txt
git commit -m "Add feature change 1"

echo "Feature change 2" > feature.txt
git add feature.txt
git commit -m "Add feature change 2"

# Switch back to main and make a conflicting change
git checkout main
echo "Main change 1" > main.txt
git add main.txt
git commit -m "Add main change 1"

echo "Main change 2" > main.txt
git add main.txt
git commit -m "Add main change 2"

# Now, let's try merging the feature branch into main
git merge feature

If you run this, Git will likely default to the recursive strategy because it’s the most common and versatile. You’ll see output like this:

Auto-merging feature.txt
CONFLICT (content): Merge conflict in feature.txt
Automatic merge failed; fix conflicts and then commit the result.

This is where the strategies come into play. The recursive strategy is Git’s default for merging two branches. It intelligently looks for a common ancestor and then figures out how to combine the changes from both branches. If there are overlapping changes, it flags them as conflicts, as you saw above.

To resolve the conflict, you’d manually edit feature.txt to look how you want it (e.g., keeping both sets of changes or picking one), then:

git add feature.txt
git commit -m "Merge feature branch with conflict resolution"

Now, let’s explore other strategies. What if you want to discard all changes made on the feature branch and just keep what’s on main? That’s where ours comes in.

# Reset our scenario to before the merge attempt
git reset --hard HEAD~2 # Go back to before the main changes
git checkout main
git branch -D feature # Delete the old feature branch
git checkout -b feature
echo "Feature change 1" > feature.txt
git add feature.txt
git commit -m "Add feature change 1"
echo "Feature change 2" > feature.txt
git add feature.txt
git commit -m "Add feature change 2"
git checkout main
echo "Main change 1" > main.txt
git add main.txt
git commit -m "Add main change 1"

# Now merge with the 'ours' strategy
git merge -s ours feature

The output will be much simpler:

Updating 1234567..abcdef0
Fast-forward (or something similar if not a fast-forward)

And feature.txt will contain only the "Main change 2" content. The ours strategy tells Git: "When merging feature into main, if there are any differences, take the version from main (the 'ours' branch from Git’s perspective in this merge direction)." It essentially overwrites the history of the merged branch with the history of the target branch.

Conversely, theirs does the opposite: it discards all changes on main and keeps only what’s on feature.

# Reset again
git reset --hard HEAD~1 # Go back to before the main change
git checkout main
git branch -D feature
git checkout -b feature
echo "Feature change 1" > feature.txt
git add feature.txt
git commit -m "Add feature change 1"
echo "Feature change 2" > feature.txt
git add feature.txt
git commit -m "Add feature change 2"
git checkout main

# Now merge with the 'theirs' strategy
git merge -s theirs feature

After this, feature.txt will contain the content from your feature branch changes, and main.txt will be gone or its content replaced by the feature branch’s state. The theirs strategy dictates: "When merging feature into main, take the version from feature (the 'theirs' branch)."

The octopus strategy is for merging more than two branches at once. It’s what Git uses by default when you try to merge multiple branches into one.

# Create a few more branches
git checkout main
git branch branch1
git branch branch2
git branch branch3

# Make changes on each branch
echo "Branch 1 change" > file1.txt
git add file1.txt
git commit -m "Add branch 1 change"

echo "Branch 2 change" > file2.txt
git add file2.txt
git commit -m "Add branch 2 change"

echo "Branch 3 change" > file3.txt
git add file3.txt
git commit -m "Add branch 3 change"

# Merge all branches into main using octopus
git checkout main
git merge branch1 branch2 branch3

Git’s octopus strategy attempts a single commit that brings together all the histories. It’s designed for situations where you’re merging several independent lines of development simultaneously. If it encounters conflicts across multiple branches, it will flag them, but it’s generally best used when branches are relatively independent to avoid complex, multi-branch conflicts.

The most surprising thing about these strategies is how ours and theirs don’t actually refer to the branches you’re currently on, but rather the order in which you list them in the merge command. When you run git merge -s ours feature, main is considered "ours" and feature is "theirs." If you were to run git merge -s theirs feature (which is equivalent to git merge feature if feature is ahead of main), feature would be "theirs." The context of the merge direction is crucial.

The next thing you’ll likely encounter is how to handle complex merge conflicts that span multiple files and branches simultaneously, especially when using the recursive strategy on large projects.

Want structured learning?

Take the full Git course →