488 lines
9.9 KiB
Markdown
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!
|