Module 04: Merge Conflicts
Learning Objectives
By the end of this module, you will:
- Understand what merge conflicts are and why they occur
- Use
git diffto discover changes between branches - Identify merge conflicts in your repository
- Read and interpret conflict markers (
<<<<<<<,=======,>>>>>>>) - Resolve merge conflicts manually
- Complete a merge after resolving conflicts
Setup
Create the challenge environment:
.\setup.ps1
This creates a repository with two feature branches that have conflicting changes.
Overview
A merge conflict occurs when git cannot automatically combine changes because both branches modified the same part of the same file in different ways.
When do conflicts happen?
- ✅ Two branches modify the same lines in a file
- ✅ One branch deletes a file that another branch modifies
- ✅ Complex changes git can't merge automatically
- ❌ Different files are changed (no conflict!)
- ❌ Different parts of the same file are changed (no conflict!)
Don't fear conflicts! They're a normal part of collaborative development. git just needs your help to decide what the final code should look like.
Your Task
Part 1: Discover the Changes
Before merging, it's good practice to see what each branch changed:
cd challenge
# Check which branch you're on
git branch
# View all branches
git branch --all
You'll see three branches: main, add-timeout, and add-debug.
Discover what each branch changed:
# Compare main with add-timeout
git diff main add-timeout
# Compare main with add-debug
git diff main add-debug
# Compare the two feature branches directly
git diff add-timeout add-debug
What did you discover?
- Both branches modified
config.json - They both added a line in the same location (after
"port": 3000) - One adds
"timeout": 5000 - The other adds
"debug": true
This is a recipe for a conflict!
Part 2: Merge the First Branch (No Conflict)
Let's merge add-timeout first:
# Make sure you're on main
git switch main
# Merge the first branch
git merge add-timeout
✅ Success! This merge works because main hasn't changed since add-timeout was created.
# View the updated config
cat config.json
# Check the history
git log --oneline --graph --all
Part 3: Try to Merge the Second Branch (Conflict!)
Now let's try to merge add-debug:
# Still on main
git merge add-debug
💥 Boom! You'll see:
Auto-merging config.json
CONFLICT (content): Merge conflict in config.json
Automatic merge failed; fix conflicts and then commit the result.
Don't panic! This is expected. Git is asking for your help.
Part 4: Check the Status
git status
You'll see:
On branch main
You have unmerged paths.
(fix conflicts and run "git commit")
(use "git merge --abort" to abort the merge)
Unmerged paths:
(use "git add <file>..." to mark resolution)
both modified: config.json
This tells you that config.json needs your attention!
Part 5: Open and Examine the Conflicted File
Open config.json in your text editor:
# On Windows
notepad config.json
# Or use VS Code
code config.json
You'll see special conflict markers:
{
"app": {
"name": "MyApp",
"version": "1.0.0",
"port": 3000,
<<<<<<< HEAD
"timeout": 5000
=======
"debug": true
>>>>>>> add-debug
}
}
Part 6: Understand the Conflict Markers
Let's break down what you're seeing:
<<<<<<< HEAD
"timeout": 5000 ← Your current branch (main, which has add-timeout merged)
=======
"debug": true ← The branch you're merging (add-debug)
>>>>>>> add-debug
What each marker means:
<<<<<<< HEAD- Start of your changes (current branch)=======- Separator between the two versions>>>>>>> add-debug- End of their changes (branch being merged)
Part 7: Resolve the Conflict
You have three options:
Option 1: Keep ONLY your changes (timeout)
"timeout": 5000
Option 2: Keep ONLY their changes (debug)
"debug": true
Option 3: Keep BOTH changes ← Do this!
"timeout": 5000,
"debug": true
Part 8: Edit the File
For this challenge, we want both settings, so:
-
Delete ALL the conflict markers:
- Remove
<<<<<<< HEAD - Remove
======= - Remove
>>>>>>> add-debug
- Remove
-
Keep both settings:
Before (with conflict markers):
{
"app": {
"name": "MyApp",
"version": "1.0.0",
"port": 3000,
<<<<<<< HEAD
"timeout": 5000
=======
"debug": true
>>>>>>> add-debug
}
}
After (resolved):
{
"app": {
"name": "MyApp",
"version": "1.0.0",
"port": 3000,
"timeout": 5000,
"debug": true
}
}
Important:
- Remove ALL markers
- Add a comma after
"timeout": 5000(for valid JSON) - Ensure the file is valid JSON
- Save the file
Part 9: Mark the Conflict as Resolved
Tell git you've resolved the conflict:
# Stage the resolved file
git add config.json
# Check status
git status
You should see:
On branch main
All conflicts fixed but you are still merging.
(use "git commit" to conclude merge)
Perfect! git confirms the conflict is resolved.
Part 10: Complete the Merge
Commit the merge:
git commit
git will open an editor with a default merge message. You can accept it or customize it, then save and close.
Done! Your merge is complete!
# View the final result
cat config.json
# View the history
git log --oneline --graph --all
You should see both timeout and debug in the config!
Part 11: Verify Your Solution
From the module directory (not inside challenge/):
.\verify.ps1
Understanding Conflict Markers
Anatomy of a Conflict
<<<<<<< HEAD ← Marker: Start of your version
Your changes here
======= ← Marker: Separator
Their changes here
>>>>>>> branch-name ← Marker: End of their version
Common Conflict Patterns
Simple conflict:
<<<<<<< HEAD
print("Hello")
=======
print("Hi")
>>>>>>> feature
Decision: Which greeting do you want?
Both are needed:
<<<<<<< HEAD
timeout: 5000
=======
debug: true
>>>>>>> feature
Decision: Keep both (add comma)!
Deletion conflict:
<<<<<<< HEAD
# Function deleted on your branch
=======
def old_function():
pass
>>>>>>> feature
Decision: Delete or keep the function?
Common Mistakes to Avoid
❌ Forgetting to remove conflict markers
<<<<<<< HEAD ← Don't leave these in!
"timeout": 5000,
"debug": true
>>>>>>> add-debug ← Don't leave these in!
This breaks your code! Always remove ALL markers.
❌ Committing without staging
git commit # Error! You didn't add the file
Always git add the resolved file first!
❌ Keeping only one side when both are needed If you delete one setting, you lose that work!
❌ Breaking syntax
"timeout": 5000 ← Missing comma!
"debug": true
Always verify your file is valid after resolving!
❌ Not testing the result Always check that your resolved code works!
Aborting a Merge
Changed your mind? You can abort the merge anytime before committing:
git merge --abort
This returns your repository to the state before you started the merge. No harm done!
Key Commands
# Discover changes before merging
git diff branch1 branch2
# Attempt a merge
git merge <branch-name>
# Check which files have conflicts
git status
# Abort the merge and start over
git merge --abort
# After resolving conflicts:
git add <resolved-file>
git commit
# View conflicts in a different style
git diff --ours # Your changes
git diff --theirs # Their changes
git diff --base # Original version
Pro Tips
💡 Use git diff first
Always compare branches before merging:
git diff main..feature-branch
💡 Prevent conflicts
- Pull changes frequently
- Communicate with your team about who's working on what
- Keep branches short-lived and merge often
💡 Make conflicts easier
- Work on different files when possible
- Make small, focused commits
- If editing the same file, coordinate with teammates
💡 When stuck
- Read the conflict markers carefully
- Look at
git logto understand what each side changed - Use
git diffto see the changes - Ask a teammate to review your resolution
- Use a merge tool:
git mergetool
Merge Tools
git supports visual merge tools that make resolving conflicts easier:
# Configure a merge tool (one-time setup)
git config --global merge.tool vscode # or meld, kdiff3, etc.
# Use the merge tool during a conflict
git mergetool
This opens a visual interface showing both versions side-by-side.
Real-World Scenario
This exercise simulates a common real-world situation:
Scenario: Two developers working on the same file
- Alice adds a timeout configuration
- Bob adds debug mode configuration
- Both push their changes
- When Bob tries to merge, he gets a conflict
- Bob resolves it by keeping both changes
- Everyone's work is preserved!
This happens all the time in team development. Conflicts are normal!
What You've Learned
After completing this module, you understand:
- ✅ Merge conflicts happen when the same lines are changed differently
- ✅
git diffhelps you discover changes before merging - ✅ Conflict markers show both versions
- ✅ You decide what the final code should look like
- ✅ Remove all markers before committing
- ✅ Test your resolution to ensure it works
- ✅ Conflicts are normal and easy to resolve with practice
Next Steps
Ready to continue? The next module covers cherry-picking - selectively applying specific commits from one branch to another.
To start over:
.\reset.ps1
.\setup.ps1
Need help? Review the steps above, or run git status to see what git suggests!