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

488 lines
9.9 KiB
Markdown

# 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:
```bash
.\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:
```bash
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:**
```bash
# 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:
```bash
# 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.
```bash
# 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`:
```bash
# 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
```bash
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:
```bash
# On Windows
notepad config.json
# Or use VS Code
code config.json
```
You'll see special **conflict markers**:
```json
{
"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)**
```json
"timeout": 5000
```
**Option 2: Keep ONLY their changes (debug)**
```json
"debug": true
```
**Option 3: Keep BOTH changes****Do this!**
```json
"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):**
```json
{
"app": {
"name": "MyApp",
"version": "1.0.0",
"port": 3000,
<<<<<<< HEAD
"timeout": 5000
=======
"debug": true
>>>>>>> add-debug
}
}
```
**After (resolved):**
```json
{
"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
3. Save the file
### Part 9: Mark the Conflict as Resolved
Tell Git you've resolved the conflict:
```bash
# 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:
```bash
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!
```bash
# 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/):
```bash
.\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**
```json
<<<<<<< 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**
```bash
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**
```json
"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:
```bash
git merge --abort
```
This returns your repository to the state before you started the merge. No harm done!
## Key Commands
```bash
# 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:
```bash
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:
```bash
# 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:
```bash
.\reset.ps1
.\setup.ps1
```
**Need help?** Review the steps above, or run `git status` to see what Git suggests!