Files
git-workshop/01-essentials/04-merge-conflict/README.md
2026-01-15 12:51:43 +01:00

9.9 KiB

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 diff to 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 changesDo this!

    "timeout": 5000,
    "debug": true

Part 8: Edit the File

For this challenge, we want both settings, so:

  1. Delete ALL the conflict markers:

    • Remove <<<<<<< HEAD
    • Remove =======
    • Remove >>>>>>> add-debug
  2. 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
  1. 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 log to understand what each side changed
  • Use git diff to 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 diff helps 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!