feat: add multiplayer guidelines

This commit is contained in:
Bjarke Sporring
2026-01-07 23:46:32 +01:00
parent eb63970b7a
commit 7f34cf2d08
30 changed files with 2312 additions and 595 deletions

View File

@@ -0,0 +1,448 @@
# Module 06: Merge Strategies - Fast-Forward vs Three-Way
## Learning Objectives
In this module, you will:
- Understand the difference between fast-forward and three-way merges
- Learn when Git automatically chooses each strategy
- Force specific merge behavior with `--no-ff` and `--ff-only` flags
- Understand the trade-offs between linear and branched history
- Make informed decisions about merge strategies for different workflows
## Prerequisites
Before starting this module, you should have completed:
- Module 03: Branching Basics
- Module 04: Merging Branches
## Challenge
### Setup
Run the setup script to create your challenge environment:
```powershell
.\setup.ps1
```
This will create a `challenge/` directory with scenarios for both fast-forward and three-way merges.
### Your Task
You'll experiment with both types of merges and learn how to control Git's merge behavior.
**Part 1: Fast-Forward Merge**
1. Merge `feature-fast-forward` into main
2. Observe Git's "Fast-forward" message
3. Examine the linear history
**Part 2: Three-Way Merge**
4. Merge `feature-divergent` into main
5. Observe Git creates a merge commit
6. Examine the branched history
**Part 3: Force Merge Commit**
7. Merge `feature-optional` into main using `--no-ff`
8. Compare with the fast-forward from Part 1
**Steps:**
1. Navigate to the challenge directory: `cd challenge`
2. View all branches: `git branch -a`
3. View the current graph: `git log --oneline --graph --all`
4. Merge feature-fast-forward: `git merge feature-fast-forward`
5. Check the log: `git log --oneline --graph`
6. Merge feature-divergent: `git merge feature-divergent`
7. Check the log: `git log --oneline --graph --all`
8. Merge feature-optional with --no-ff: `git merge --no-ff feature-optional`
9. Compare the results: `git log --oneline --graph --all`
> **Key Questions to Consider:**
> - Which merges created merge commits?
> - Which merge kept a linear history?
> - How does `--no-ff` change Git's behavior?
> - When would you prefer each approach?
## Understanding Merge Strategies
### Fast-Forward Merge
A **fast-forward merge** happens when the target branch (e.g., `main`) hasn't changed since the feature branch was created. Git simply "fast-forwards" the branch pointer to the latest commit on the feature branch.
**Before the merge:**
```
main: A---B
\
feature: C---D
```
In this scenario:
- Commit B is where `feature` branched off from `main`
- Commits C and D are new commits on the `feature` branch
- `main` has NO new commits since the branch split
- The history is **linear** (straight line from A to D)
**After `git merge feature` (on main):**
```
main: A---B---C---D
feature
```
**What happened:**
- Git moved the `main` pointer forward to commit D
- NO merge commit was created
- The history remains linear (a straight line)
- Both `main` and `feature` now point to the same commit (D)
**Command:**
```bash
git switch main
git merge feature-fast-forward
```
**Output you'll see:**
```
Updating abc123..def456
Fast-forward
new-feature.py | 10 ++++++++++
1 file changed, 10 insertions(+)
```
**Notice the "Fast-forward" message!**
**Characteristics:**
- ✅ Keeps history linear and clean
- ✅ Simpler to read in `git log`
- ✅ No extra merge commit
- ❌ Loses visibility that work was done on a branch
- ❌ Harder to revert entire features at once
---
### Three-Way Merge
A **three-way merge** happens when BOTH branches have new commits since they diverged. Git must combine changes from both branches, which creates a special merge commit.
**Before the merge:**
```
main: A---B---C---E
\
feature: D---F
```
In this scenario:
- Commit B is where `feature` branched off from `main`
- Commits C and E are new commits on `main`
- Commits D and F are new commits on `feature`
- The branches have **diverged** (both have unique commits)
**After `git merge feature` (on main):**
```
main: A---B---C---E---M
\ /
feature: D---F---/
```
**What happened:**
- Git created a new **merge commit** (M)
- Commit M has TWO parent commits: E (from main) and F (from feature)
- The merge commit combines changes from both branches
- The history shows the branches converging
**Why it's called "three-way":**
Git uses THREE commits to perform the merge:
1. **Commit B** - The common ancestor (where branches split)
2. **Commit E** - The latest commit on `main`
3. **Commit F** - The latest commit on `feature`
Git compares all three to figure out what changed on each branch and how to combine them.
**Command:**
```bash
git switch main
git merge feature-divergent
```
**Output you'll see:**
```
Merge made by the 'ort' strategy.
feature-code.py | 5 +++++
1 file changed, 5 insertions(+)
```
**Notice it says "Merge made by the 'ort' strategy" instead of "Fast-forward"!**
**Characteristics:**
- ✅ Preserves feature branch history
- ✅ Shows when features were merged
- ✅ Easier to revert entire features (revert the merge commit)
- ✅ Clear visualization in git log --graph
- ❌ Creates more commits (merge commits)
- ❌ History can become complex with many merges
---
### When Does Each Type Happen?
| Situation | Merge Type | Merge Commit? | Git's Behavior |
|-----------|------------|---------------|----------------|
| Target branch (main) has NO new commits | Fast-Forward | ❌ No | Automatic |
| Target branch (main) HAS new commits | Three-Way | ✅ Yes | Automatic |
| You use `--no-ff` flag | Three-Way | ✅ Yes | Forced |
| You use `--ff-only` flag | Fast-Forward | ❌ No | Fails if not possible |
**Git chooses automatically** based on the branch state, but you can override this behavior!
---
## Controlling Merge Behavior
### Force a Merge Commit with `--no-ff`
Even if a fast-forward is possible, you can force Git to create a merge commit:
```bash
git merge --no-ff feature-optional
```
**Before (fast-forward would be possible):**
```
main: A---B
\
feature: C---D
```
**After `git merge --no-ff feature` (on main):**
```
main: A---B-------M
\ /
feature: C---D
```
Notice that even though main didn't change, a merge commit (M) was created!
**When to use `--no-ff`:**
- ✅ When you want to preserve the feature branch in history
- ✅ For important features that might need to be reverted
- ✅ In team workflows where you want to see when features were merged
- ✅ When following a branching model like Git Flow
**Example:**
```bash
# You've finished a major feature on feature-auth
git switch main
git merge --no-ff feature-auth -m "Merge feature-auth: Add user authentication system"
```
This creates a clear marker in history showing when and what was merged.
---
### Require Fast-Forward with `--ff-only`
You can make Git fail the merge if a fast-forward isn't possible:
```bash
git merge --ff-only feature-branch
```
**What happens:**
- ✅ If fast-forward is possible, Git merges
- ❌ If branches have diverged, Git refuses to merge
**Output when it fails:**
```
fatal: Not possible to fast-forward, aborting.
```
**When to use `--ff-only`:**
- ✅ When you want to keep history strictly linear
- ✅ To ensure you rebase before merging
- ✅ In workflows that prohibit merge commits
**Example workflow:**
```bash
# Try to merge
git merge --ff-only feature-branch
# If it fails, rebase first
git switch feature-branch
git rebase main
git switch main
git merge --ff-only feature-branch # Now it works!
```
---
## Visualizing the Difference
### Linear History (Fast-Forward)
```bash
git log --oneline --graph
```
```
* d1e2f3g (HEAD -> main, feature) Add feature C
* a4b5c6d Add feature B
* 7e8f9g0 Add feature A
* 1a2b3c4 Initial commit
```
Notice the straight line! No branching visible.
---
### Branched History (Three-Way Merge)
```bash
git log --oneline --graph --all
```
```
* m1e2r3g (HEAD -> main) Merge branch 'feature'
|\
| * f4e5a6t (feature) Add feature implementation
| * u7r8e9s Feature setup
* | a1b2c3d Update documentation on main
|/
* 0i1n2i3t Initial commit
```
Notice the branching pattern! You can see:
- Where the branch split (`|/`)
- Commits on each branch (`|`)
- Where branches merged (`* merge commit`)
---
## Decision Guide: Which Strategy to Use?
### Use Fast-Forward When:
- ✅ Working on personal projects
- ✅ Want simplest, cleanest history
- ✅ Small changes or bug fixes
- ✅ Don't need to track feature branches
- ✅ History readability is top priority
### Use Three-Way Merge (or force with --no-ff) When:
- ✅ Working in teams
- ✅ Want to preserve feature context
- ✅ Need to revert features as units
- ✅ Following Git Flow or similar workflow
- ✅ Important to see when features were integrated
### Force Fast-Forward (--ff-only) When:
- ✅ Enforcing rebase workflow
- ✅ Maintaining strictly linear history
- ✅ Integration branch requires clean history
---
## Comparison Table
| Aspect | Fast-Forward | Three-Way |
|--------|-------------|-----------|
| **When it happens** | Target branch unchanged | Both branches have new commits |
| **Merge commit created?** | ❌ No | ✅ Yes |
| **History appearance** | Linear | Branched |
| **Command output** | "Fast-forward" | "Merge made by..." |
| **Git log --graph** | Straight line | Fork and merge pattern |
| **Can revert entire feature?** | ❌ No (must revert each commit) | ✅ Yes (revert merge commit) |
| **Force it** | `--ff-only` | `--no-ff` |
| **Best for** | Solo work, small changes | Team work, features |
---
## Useful Commands
### Merging with Strategy Control
```bash
git merge <branch> # Let Git decide automatically
git merge --no-ff <branch> # Force a merge commit
git merge --ff-only <branch> # Only merge if fast-forward possible
git merge --abort # Cancel a merge in progress
```
### Viewing Different Merge Types
```bash
# See all merges
git log --merges # Only merge commits
git log --no-merges # Hide merge commits
# Visualize history
git log --oneline --graph # See branch structure
git log --oneline --graph --all # Include all branches
git log --first-parent # Follow only main branch line
# Check if merge would be fast-forward
git merge-base main feature # Find common ancestor
git log main..feature # See commits unique to feature
```
### Configuring Default Behavior
```bash
# Disable fast-forward by default
git config merge.ff false # Always create merge commits
# Only allow fast-forward
git config merge.ff only # Refuse non-fast-forward merges
# Reset to default
git config --unset merge.ff
```
---
## Common Patterns in the Wild
### GitHub Flow (Simple)
- Use fast-forward when possible
- Short-lived feature branches
- Merge to main frequently
### Git Flow (Structured)
- Always use `--no-ff` for features
- Preserve branch history
- Complex release management
### Rebase Workflow
- Always use `--ff-only`
- Rebase before merging
- Strictly linear history
---
## Verification
Once you've completed all three merges, verify your solution:
```powershell
.\verify.ps1
```
The verification script will check that you've experienced both types of merges and used the `--no-ff` flag.
## Need to Start Over?
If you want to reset the challenge and start fresh:
```powershell
.\reset.ps1
```
This will remove the challenge directory and run the setup script again, giving you a clean slate.
## What's Next?
Now that you understand merge strategies, you can make informed decisions about your workflow. Consider:
- **For personal projects:** Fast-forward merges keep history simple
- **For team projects:** Three-way merges preserve context
- **For open source:** Follow the project's contribution guidelines
The best strategy depends on your team's needs and workflow!

View File

@@ -0,0 +1,24 @@
#!/usr/bin/env pwsh
<#
.SYNOPSIS
Resets the Module 06 challenge environment.
.DESCRIPTION
This script removes the challenge directory and re-runs the setup script
to give you a fresh start.
#>
Write-Host "`n=== Resetting Module 06 Challenge ===" -ForegroundColor Cyan
# Remove challenge directory if it exists
if (Test-Path "challenge") {
Write-Host "Removing existing challenge directory..." -ForegroundColor Yellow
Remove-Item -Recurse -Force "challenge"
Write-Host "Challenge directory removed." -ForegroundColor Green
} else {
Write-Host "No challenge directory found to remove." -ForegroundColor Yellow
}
# Run setup script
Write-Host "`nRunning setup script..." -ForegroundColor Cyan
& "./setup.ps1"

View File

@@ -0,0 +1,221 @@
#!/usr/bin/env pwsh
<#
.SYNOPSIS
Sets up the Module 06 challenge environment for learning merge strategies.
.DESCRIPTION
This script creates a challenge directory with a Git repository that
contains scenarios for both fast-forward and three-way merges, allowing
students to compare different merge strategies.
#>
Write-Host "`n=== Setting up Module 06 Challenge ===" -ForegroundColor Cyan
# Remove existing challenge directory if it exists
if (Test-Path "challenge") {
Write-Host "Removing existing challenge directory..." -ForegroundColor Yellow
Remove-Item -Recurse -Force "challenge"
}
# Create fresh challenge directory
Write-Host "Creating challenge directory..." -ForegroundColor Green
New-Item -ItemType Directory -Path "challenge" | Out-Null
Set-Location "challenge"
# Initialize Git repository
Write-Host "Initializing Git repository..." -ForegroundColor Green
git init | Out-Null
# Configure git for this repository
git config user.name "Workshop Student"
git config user.email "student@example.com"
# ========================================
# Scenario 1: Fast-Forward Merge Setup
# ========================================
Write-Host "Setting up fast-forward merge scenario..." -ForegroundColor Green
# Commit 1: Initial structure on main
$appContent = @"
# app.py - Main application
def main():
print("Application started")
pass
if __name__ == "__main__":
main()
"@
Set-Content -Path "app.py" -Value $appContent
git add .
git commit -m "Initial application structure" | Out-Null
# Create feature-fast-forward branch (main won't change after this)
git switch -c feature-fast-forward | Out-Null
# Commit on feature-fast-forward
$utilsContent = @"
# utils.py - Utility functions
def format_string(text):
"""Format a string to title case."""
return text.title()
def validate_input(text):
"""Validate user input."""
return text and len(text) > 0
"@
Set-Content -Path "utils.py" -Value $utilsContent
git add .
git commit -m "Add utility functions" | Out-Null
# Second commit on feature-fast-forward
$utilsContent = @"
# utils.py - Utility functions
def format_string(text):
"""Format a string to title case."""
return text.title()
def validate_input(text):
"""Validate user input."""
return text and len(text) > 0
def sanitize_input(text):
"""Remove dangerous characters from input."""
return text.replace("<", "").replace(">", "")
"@
Set-Content -Path "utils.py" -Value $utilsContent
git add .
git commit -m "Add input sanitization" | Out-Null
# ========================================
# Scenario 2: Three-Way Merge Setup
# ========================================
Write-Host "Setting up three-way merge scenario..." -ForegroundColor Green
# Switch back to main
git switch main | Out-Null
# Create feature-divergent branch
git switch -c feature-divergent | Out-Null
# Commit on feature-divergent
$authContent = @"
# auth.py - Authentication module
def authenticate(username, password):
"""Authenticate a user."""
print(f"Authenticating: {username}")
# TODO: Implement actual authentication
return True
"@
Set-Content -Path "auth.py" -Value $authContent
git add .
git commit -m "Add authentication module" | Out-Null
# Second commit on feature-divergent
$authContent = @"
# auth.py - Authentication module
def authenticate(username, password):
"""Authenticate a user."""
print(f"Authenticating: {username}")
# TODO: Implement actual authentication
return True
def check_permissions(user, resource):
"""Check if user has permission for resource."""
print(f"Checking permissions for {user}")
return True
"@
Set-Content -Path "auth.py" -Value $authContent
git add .
git commit -m "Add permission checking" | Out-Null
# Switch back to main and make a commit (creates divergence)
git switch main | Out-Null
$readmeContent = @"
# My Application
A Python application with utilities and authentication.
## Features
- String formatting and validation
- User authentication
- Permission management
## Setup
1. Install Python 3.8+
2. Run: python app.py
"@
Set-Content -Path "README.md" -Value $readmeContent
git add .
git commit -m "Add README documentation" | Out-Null
# ========================================
# Scenario 3: Optional Fast-Forward for --no-ff
# ========================================
Write-Host "Setting up --no-ff demonstration scenario..." -ForegroundColor Green
# Create feature-optional branch (main won't change after this)
git switch -c feature-optional | Out-Null
# Commit on feature-optional
$configContent = @"
# config.py - Configuration settings
DEBUG_MODE = False
LOG_LEVEL = "INFO"
DATABASE_URL = "sqlite:///app.db"
"@
Set-Content -Path "config.py" -Value $configContent
git add .
git commit -m "Add configuration module" | Out-Null
# Switch back to main
git switch main | Out-Null
# Return to module directory
Set-Location ..
Write-Host "`n=== Setup Complete! ===" -ForegroundColor Green
Write-Host "`nYour challenge environment is ready in the 'challenge/' directory." -ForegroundColor Cyan
Write-Host "`nYou have THREE scenarios set up:" -ForegroundColor Yellow
Write-Host "`n Scenario 1: Fast-Forward Merge" -ForegroundColor White
Write-Host " Branch: feature-fast-forward" -ForegroundColor Cyan
Write-Host " Status: main has NOT changed since branch was created" -ForegroundColor Cyan
Write-Host " Result: Will fast-forward (no merge commit)" -ForegroundColor Green
Write-Host "`n Scenario 2: Three-Way Merge" -ForegroundColor White
Write-Host " Branch: feature-divergent" -ForegroundColor Cyan
Write-Host " Status: BOTH main and branch have new commits" -ForegroundColor Cyan
Write-Host " Result: Will create merge commit" -ForegroundColor Green
Write-Host "`n Scenario 3: Force Merge Commit" -ForegroundColor White
Write-Host " Branch: feature-optional" -ForegroundColor Cyan
Write-Host " Status: Could fast-forward, but we'll use --no-ff" -ForegroundColor Cyan
Write-Host " Result: Will create merge commit even though fast-forward is possible" -ForegroundColor Green
Write-Host "`nNext steps:" -ForegroundColor Cyan
Write-Host " 1. cd challenge" -ForegroundColor White
Write-Host " 2. View initial state: git log --oneline --graph --all" -ForegroundColor White
Write-Host " 3. Merge fast-forward: git merge feature-fast-forward" -ForegroundColor White
Write-Host " 4. View result: git log --oneline --graph" -ForegroundColor White
Write-Host " 5. Merge divergent: git merge feature-divergent" -ForegroundColor White
Write-Host " 6. View result: git log --oneline --graph --all" -ForegroundColor White
Write-Host " 7. Merge with --no-ff: git merge --no-ff feature-optional" -ForegroundColor White
Write-Host " 8. View final result: git log --oneline --graph --all" -ForegroundColor White
Write-Host " 9. Compare all three merges!" -ForegroundColor White
Write-Host " 10. Run '..\verify.ps1' to check your solution" -ForegroundColor White
Write-Host ""

View File

@@ -0,0 +1,140 @@
#!/usr/bin/env pwsh
<#
.SYNOPSIS
Verifies the Module 06 challenge solution.
.DESCRIPTION
This script checks that:
- The challenge directory exists
- A Git repository exists
- All three feature branches have been merged
- Appropriate merge strategies were used
- Student understands the difference between fast-forward and three-way merges
#>
Write-Host "`n=== Verifying Module 06 Solution ===" -ForegroundColor Cyan
$allChecksPassed = $true
# Check if challenge directory exists
if (-not (Test-Path "challenge")) {
Write-Host "[FAIL] Challenge directory not found. Did you run setup.ps1?" -ForegroundColor Red
exit 1
}
Set-Location "challenge"
# Check if git repository exists
if (-not (Test-Path ".git")) {
Write-Host "[FAIL] Not a git repository. Did you run setup.ps1?" -ForegroundColor Red
Set-Location ..
exit 1
}
# Check current branch is main
$currentBranch = git branch --show-current 2>$null
if ($currentBranch -eq "main") {
Write-Host "[PASS] Currently on main branch" -ForegroundColor Green
} else {
Write-Host "[FAIL] Not on main branch (currently on: $currentBranch)" -ForegroundColor Red
Write-Host "[HINT] Switch to main with: git switch main" -ForegroundColor Yellow
$allChecksPassed = $false
}
# Check 1: Fast-Forward Merge - utils.py should exist
if (Test-Path "utils.py") {
Write-Host "[PASS] utils.py exists (feature-fast-forward merged)" -ForegroundColor Green
} else {
Write-Host "[FAIL] utils.py not found" -ForegroundColor Red
Write-Host "[HINT] Merge feature-fast-forward: git merge feature-fast-forward" -ForegroundColor Yellow
$allChecksPassed = $false
}
# Check 2: Three-Way Merge - auth.py should exist
if (Test-Path "auth.py") {
Write-Host "[PASS] auth.py exists (feature-divergent merged)" -ForegroundColor Green
} else {
Write-Host "[FAIL] auth.py not found" -ForegroundColor Red
Write-Host "[HINT] Merge feature-divergent: git merge feature-divergent" -ForegroundColor Yellow
$allChecksPassed = $false
}
# Check 3: --no-ff Merge - config.py should exist
if (Test-Path "config.py") {
Write-Host "[PASS] config.py exists (feature-optional merged)" -ForegroundColor Green
} else {
Write-Host "[FAIL] config.py not found" -ForegroundColor Red
Write-Host "[HINT] Merge feature-optional: git merge --no-ff feature-optional" -ForegroundColor Yellow
$allChecksPassed = $false
}
# Check for merge commits
$mergeCommits = git log --merges --oneline 2>$null
$mergeCount = ($mergeCommits | Measure-Object -Line).Lines
if ($mergeCount -ge 2) {
Write-Host "[PASS] Found $mergeCount merge commit(s)" -ForegroundColor Green
Write-Host "[INFO] Expected: 1 from three-way merge + 1 from --no-ff = 2 total" -ForegroundColor Cyan
} else {
Write-Host "[FAIL] Only found $mergeCount merge commit(s), expected at least 2" -ForegroundColor Red
Write-Host "[HINT] Make sure to:" -ForegroundColor Yellow
Write-Host " 1. Merge feature-divergent (creates merge commit)" -ForegroundColor Yellow
Write-Host " 2. Merge feature-optional with --no-ff flag (forces merge commit)" -ForegroundColor Yellow
$allChecksPassed = $false
}
# Provide detailed merge analysis
Write-Host "`n--- Merge Analysis ---" -ForegroundColor Cyan
# Count total commits
$totalCommits = git rev-list --count HEAD 2>$null
Write-Host "[INFO] Total commits on main: $totalCommits" -ForegroundColor Cyan
# List merge commits
if ($mergeCommits) {
Write-Host "[INFO] Merge commits found:" -ForegroundColor Cyan
$mergeCommits | ForEach-Object {
Write-Host " $_" -ForegroundColor White
}
}
# Check if branches still exist
$branches = git branch 2>$null
Write-Host "[INFO] Existing branches:" -ForegroundColor Cyan
$branches | ForEach-Object {
Write-Host " $_" -ForegroundColor White
}
Set-Location ..
# Final summary
if ($allChecksPassed) {
Write-Host "`n" -NoNewline
Write-Host "=====================================" -ForegroundColor Green
Write-Host " CONGRATULATIONS! CHALLENGE PASSED!" -ForegroundColor Green
Write-Host "=====================================" -ForegroundColor Green
Write-Host "`nYou've mastered Git merge strategies!" -ForegroundColor Cyan
Write-Host "`nYou now understand:" -ForegroundColor Cyan
Write-Host " - Fast-forward merges (linear history, no merge commit)" -ForegroundColor White
Write-Host " - Three-way merges (divergent branches, creates merge commit)" -ForegroundColor White
Write-Host " - How to force merge commits with --no-ff flag" -ForegroundColor White
Write-Host " - When to use each merge strategy" -ForegroundColor White
Write-Host " - Trade-offs between linear and branched history" -ForegroundColor White
Write-Host "`nKey Takeaways:" -ForegroundColor Yellow
Write-Host " - Git chooses the strategy automatically based on branch state" -ForegroundColor Cyan
Write-Host " - Use --no-ff to preserve feature branch history" -ForegroundColor Cyan
Write-Host " - Use --ff-only to enforce linear history" -ForegroundColor Cyan
Write-Host " - Different workflows prefer different strategies" -ForegroundColor Cyan
Write-Host "`nNow you can make informed decisions about merge strategies for your projects!" -ForegroundColor Green
Write-Host ""
} else {
Write-Host "`n[SUMMARY] Some checks failed. Review the hints above and try again." -ForegroundColor Red
Write-Host "[INFO] You can run this verification script as many times as needed." -ForegroundColor Yellow
Write-Host ""
Write-Host "Quick Reference:" -ForegroundColor Cyan
Write-Host " git merge feature-fast-forward # Fast-forward merge" -ForegroundColor White
Write-Host " git merge feature-divergent # Three-way merge" -ForegroundColor White
Write-Host " git merge --no-ff feature-optional # Force merge commit" -ForegroundColor White
Write-Host ""
exit 1
}