feat: add initial module 04
This commit is contained in:
97
module-04-merging/README.md
Normal file
97
module-04-merging/README.md
Normal file
@@ -0,0 +1,97 @@
|
|||||||
|
# Module 04: Merging
|
||||||
|
|
||||||
|
## Learning Objectives
|
||||||
|
|
||||||
|
In this module, you will:
|
||||||
|
- Understand what merging means in Git
|
||||||
|
- Perform a fast-forward merge
|
||||||
|
- Perform a three-way merge
|
||||||
|
- Understand when merge commits are created
|
||||||
|
- Use `git merge` to combine branches
|
||||||
|
|
||||||
|
## Challenge
|
||||||
|
|
||||||
|
### Setup
|
||||||
|
|
||||||
|
Run the setup script to create your challenge environment:
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
.\setup.ps1
|
||||||
|
```
|
||||||
|
|
||||||
|
This will create a `challenge/` directory with a Git repository that has a main branch and a feature branch ready to merge.
|
||||||
|
|
||||||
|
### Your Task
|
||||||
|
|
||||||
|
This challenge has two parts that teach you about the two types of merges in Git:
|
||||||
|
|
||||||
|
**Part 1: Fast-Forward Merge**
|
||||||
|
1. Merge the existing `feature-api` branch into main
|
||||||
|
2. Observe that this is a "fast-forward" merge (no merge commit created)
|
||||||
|
|
||||||
|
**Part 2: Three-Way Merge**
|
||||||
|
3. Create a new branch called `feature-ui`
|
||||||
|
4. Make commits on the feature-ui branch
|
||||||
|
5. Switch back to main and make a commit there too (creates divergence)
|
||||||
|
6. Merge feature-ui into main
|
||||||
|
7. Observe that this creates a merge commit (three-way merge)
|
||||||
|
|
||||||
|
**Suggested Approach:**
|
||||||
|
|
||||||
|
1. Navigate to the challenge directory: `cd challenge`
|
||||||
|
2. Check current branch: `git branch` (should be on main)
|
||||||
|
3. View existing branches: `git branch -a`
|
||||||
|
4. Merge feature-api: `git merge feature-api`
|
||||||
|
5. View the log: `git log --oneline --graph`
|
||||||
|
6. Create feature-ui branch: `git checkout -b feature-ui`
|
||||||
|
7. Create a new file `ui.py` and commit it
|
||||||
|
8. Make another commit on feature-ui (modify ui.py)
|
||||||
|
9. Switch back to main: `git checkout main`
|
||||||
|
10. Make a change on main (modify api.py) and commit it
|
||||||
|
11. Merge feature-ui: `git merge feature-ui`
|
||||||
|
12. View the merge history: `git log --oneline --graph --all`
|
||||||
|
|
||||||
|
> **Important Notes:**
|
||||||
|
> - A **fast-forward merge** happens when main hasn't changed since the feature branch was created
|
||||||
|
> - A **three-way merge** creates a merge commit when both branches have diverged
|
||||||
|
> - You can see merge commits with `git log --merges`
|
||||||
|
> - The `--graph` option helps visualize the branch history
|
||||||
|
> - After merging, the feature branch still exists but you can delete it with `git branch -d`
|
||||||
|
|
||||||
|
## Key Concepts
|
||||||
|
|
||||||
|
- **Merge**: Combining changes from different branches into one branch.
|
||||||
|
- **Fast-Forward Merge**: When the target branch hasn't changed, Git simply moves the branch pointer forward. No merge commit is created.
|
||||||
|
- **Three-Way Merge**: When both branches have new commits, Git creates a merge commit that has two parent commits.
|
||||||
|
- **Merge Commit**: A special commit with two (or more) parent commits, representing the point where branches were merged.
|
||||||
|
- **Divergent Branches**: Branches that have different commits since they split from a common ancestor.
|
||||||
|
|
||||||
|
## Useful Commands
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git merge <branch> # Merge branch into current branch
|
||||||
|
git log --oneline --graph # View merge history visually
|
||||||
|
git log --graph --all # View all branches and merges
|
||||||
|
git log --merges # Show only merge commits
|
||||||
|
git branch -d <branch> # Delete a merged branch (optional)
|
||||||
|
```
|
||||||
|
|
||||||
|
## Verification
|
||||||
|
|
||||||
|
Once you've completed both merges, verify your solution:
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
.\verify.ps1
|
||||||
|
```
|
||||||
|
|
||||||
|
The verification script will check that you've successfully merged both feature branches and understand the different merge types.
|
||||||
|
|
||||||
|
## 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.
|
||||||
26
module-04-merging/reset.ps1
Normal file
26
module-04-merging/reset.ps1
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
#!/usr/bin/env pwsh
|
||||||
|
<#
|
||||||
|
.SYNOPSIS
|
||||||
|
Resets the Module 04 challenge environment.
|
||||||
|
|
||||||
|
.DESCRIPTION
|
||||||
|
This script removes the existing challenge directory and runs
|
||||||
|
the setup script again to create a fresh challenge environment.
|
||||||
|
#>
|
||||||
|
|
||||||
|
Write-Host "`n=== Resetting Module 04 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"
|
||||||
|
} else {
|
||||||
|
Write-Host "No existing challenge directory found." -ForegroundColor Cyan
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host "----------------------------------------" -ForegroundColor Cyan
|
||||||
|
Write-Host ""
|
||||||
|
|
||||||
|
# Run setup script
|
||||||
|
& "$PSScriptRoot\setup.ps1"
|
||||||
128
module-04-merging/setup.ps1
Normal file
128
module-04-merging/setup.ps1
Normal file
@@ -0,0 +1,128 @@
|
|||||||
|
#!/usr/bin/env pwsh
|
||||||
|
<#
|
||||||
|
.SYNOPSIS
|
||||||
|
Sets up the Module 04 challenge environment for learning about merging.
|
||||||
|
|
||||||
|
.DESCRIPTION
|
||||||
|
This script creates a challenge directory with a Git repository that
|
||||||
|
contains a main branch and a feature branch ready to merge. Students
|
||||||
|
will practice both fast-forward and three-way merges.
|
||||||
|
#>
|
||||||
|
|
||||||
|
Write-Host "`n=== Setting up Module 04 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"
|
||||||
|
|
||||||
|
# Commit 1: Initial API file on main
|
||||||
|
Write-Host "Creating initial API structure on main..." -ForegroundColor Green
|
||||||
|
$apiContent = @"
|
||||||
|
# api.py - API module
|
||||||
|
|
||||||
|
def api_handler():
|
||||||
|
print("API Handler initialized")
|
||||||
|
return True
|
||||||
|
"@
|
||||||
|
Set-Content -Path "api.py" -Value $apiContent
|
||||||
|
|
||||||
|
git add .
|
||||||
|
git commit -m "Initial API setup" | Out-Null
|
||||||
|
|
||||||
|
# Commit 2: Add more API functionality on main
|
||||||
|
$apiContent = @"
|
||||||
|
# api.py - API module
|
||||||
|
|
||||||
|
def api_handler():
|
||||||
|
print("API Handler initialized")
|
||||||
|
return True
|
||||||
|
|
||||||
|
def get_data():
|
||||||
|
print("Fetching data from API...")
|
||||||
|
return {"status": "ok"}
|
||||||
|
"@
|
||||||
|
Set-Content -Path "api.py" -Value $apiContent
|
||||||
|
|
||||||
|
git add .
|
||||||
|
git commit -m "Add get_data function" | Out-Null
|
||||||
|
|
||||||
|
# Create feature-api branch and add commits
|
||||||
|
Write-Host "Creating feature-api branch..." -ForegroundColor Green
|
||||||
|
git checkout -b feature-api 2>$null | Out-Null
|
||||||
|
|
||||||
|
# Commit on feature-api: Add API routes
|
||||||
|
$routesContent = @"
|
||||||
|
# api-routes.py - API Routes module
|
||||||
|
|
||||||
|
def setup_routes():
|
||||||
|
print("Setting up API routes...")
|
||||||
|
routes = {
|
||||||
|
"/api/data": "get_data",
|
||||||
|
"/api/status": "get_status"
|
||||||
|
}
|
||||||
|
return routes
|
||||||
|
"@
|
||||||
|
Set-Content -Path "api-routes.py" -Value $routesContent
|
||||||
|
|
||||||
|
git add .
|
||||||
|
git commit -m "Add API routes" | Out-Null
|
||||||
|
|
||||||
|
# Second commit on feature-api: Enhance routes
|
||||||
|
$routesContent = @"
|
||||||
|
# api-routes.py - API Routes module
|
||||||
|
|
||||||
|
def setup_routes():
|
||||||
|
print("Setting up API routes...")
|
||||||
|
routes = {
|
||||||
|
"/api/data": "get_data",
|
||||||
|
"/api/status": "get_status",
|
||||||
|
"/api/health": "health_check"
|
||||||
|
}
|
||||||
|
return routes
|
||||||
|
|
||||||
|
def health_check():
|
||||||
|
return {"healthy": True}
|
||||||
|
"@
|
||||||
|
Set-Content -Path "api-routes.py" -Value $routesContent
|
||||||
|
|
||||||
|
git add .
|
||||||
|
git commit -m "Add health check route" | Out-Null
|
||||||
|
|
||||||
|
# Switch back to main branch
|
||||||
|
Write-Host "Switching back to main branch..." -ForegroundColor Green
|
||||||
|
git checkout main 2>$null | 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:" -ForegroundColor Cyan
|
||||||
|
Write-Host " - A main branch with API code" -ForegroundColor White
|
||||||
|
Write-Host " - A feature-api branch with API routes ready to merge" -ForegroundColor White
|
||||||
|
Write-Host "`nNext steps:" -ForegroundColor Cyan
|
||||||
|
Write-Host " 1. cd challenge" -ForegroundColor White
|
||||||
|
Write-Host " 2. View branches: git branch -a" -ForegroundColor White
|
||||||
|
Write-Host " 3. Merge feature-api: git merge feature-api (fast-forward merge)" -ForegroundColor White
|
||||||
|
Write-Host " 4. Create feature-ui branch: git checkout -b feature-ui" -ForegroundColor White
|
||||||
|
Write-Host " 5. Make commits on feature-ui" -ForegroundColor White
|
||||||
|
Write-Host " 6. Switch back to main and make a commit there" -ForegroundColor White
|
||||||
|
Write-Host " 7. Merge feature-ui: git merge feature-ui (three-way merge)" -ForegroundColor White
|
||||||
|
Write-Host " 8. View merge history: git log --oneline --graph --all" -ForegroundColor White
|
||||||
|
Write-Host " 9. Run '..\verify.ps1' to check your solution" -ForegroundColor White
|
||||||
|
Write-Host ""
|
||||||
140
module-04-merging/verify.ps1
Normal file
140
module-04-merging/verify.ps1
Normal file
@@ -0,0 +1,140 @@
|
|||||||
|
#!/usr/bin/env pwsh
|
||||||
|
<#
|
||||||
|
.SYNOPSIS
|
||||||
|
Verifies the Module 04 challenge solution.
|
||||||
|
|
||||||
|
.DESCRIPTION
|
||||||
|
This script checks that:
|
||||||
|
- The challenge directory exists
|
||||||
|
- A Git repository exists
|
||||||
|
- Currently on main branch
|
||||||
|
- feature-api has been merged into main
|
||||||
|
- feature-ui branch exists and has been merged
|
||||||
|
- A merge commit exists (from three-way merge)
|
||||||
|
#>
|
||||||
|
|
||||||
|
Write-Host "`n=== Verifying Module 04 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 checkout main" -ForegroundColor Yellow
|
||||||
|
$allChecksPassed = $false
|
||||||
|
}
|
||||||
|
|
||||||
|
# Check if feature-api branch exists
|
||||||
|
$featureApiBranch = git branch --list "feature-api" 2>$null
|
||||||
|
if ($featureApiBranch) {
|
||||||
|
Write-Host "[PASS] Branch 'feature-api' exists" -ForegroundColor Green
|
||||||
|
} else {
|
||||||
|
Write-Host "[INFO] Branch 'feature-api' not found (may have been deleted after merge)" -ForegroundColor Cyan
|
||||||
|
}
|
||||||
|
|
||||||
|
# Check if commits from feature-api are in main
|
||||||
|
$apiRoutesCommit = git log --all --grep="Add API routes" --oneline 2>$null
|
||||||
|
$healthCheckCommit = git log --all --grep="Add health check route" --oneline 2>$null
|
||||||
|
|
||||||
|
if ($apiRoutesCommit -and $healthCheckCommit) {
|
||||||
|
# Check if these commits are in main's history
|
||||||
|
$apiRoutesInMain = git log --grep="Add API routes" --oneline 2>$null
|
||||||
|
$healthCheckInMain = git log --grep="Add health check route" --oneline 2>$null
|
||||||
|
|
||||||
|
if ($apiRoutesInMain -and $healthCheckInMain) {
|
||||||
|
Write-Host "[PASS] Commits from feature-api are merged into main" -ForegroundColor Green
|
||||||
|
} else {
|
||||||
|
Write-Host "[FAIL] Commits from feature-api not found in main" -ForegroundColor Red
|
||||||
|
Write-Host "[HINT] Merge feature-api with: git merge feature-api" -ForegroundColor Yellow
|
||||||
|
$allChecksPassed = $false
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Write-Host "[FAIL] Expected commits from feature-api not found" -ForegroundColor Red
|
||||||
|
Write-Host "[HINT] Did you run setup.ps1?" -ForegroundColor Yellow
|
||||||
|
$allChecksPassed = $false
|
||||||
|
}
|
||||||
|
|
||||||
|
# Check if api-routes.py exists (should be on main after merge)
|
||||||
|
if (Test-Path "api-routes.py") {
|
||||||
|
Write-Host "[PASS] File 'api-routes.py' exists on main (from feature-api merge)" -ForegroundColor Green
|
||||||
|
} else {
|
||||||
|
Write-Host "[FAIL] File 'api-routes.py' not found on main" -ForegroundColor Red
|
||||||
|
Write-Host "[HINT] This file should appear after merging feature-api" -ForegroundColor Yellow
|
||||||
|
$allChecksPassed = $false
|
||||||
|
}
|
||||||
|
|
||||||
|
# Check if feature-ui branch exists
|
||||||
|
$featureUiBranch = git branch --list "feature-ui" 2>$null
|
||||||
|
if ($featureUiBranch) {
|
||||||
|
Write-Host "[PASS] Branch 'feature-ui' exists" -ForegroundColor Green
|
||||||
|
|
||||||
|
# Check if feature-ui has commits
|
||||||
|
$uiCommitCount = git rev-list main..feature-ui --count 2>$null
|
||||||
|
if ($uiCommitCount -gt 0) {
|
||||||
|
Write-Host "[INFO] Branch 'feature-ui' has $uiCommitCount commit(s)" -ForegroundColor Cyan
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Write-Host "[FAIL] Branch 'feature-ui' not found" -ForegroundColor Red
|
||||||
|
Write-Host "[HINT] Create feature-ui with: git checkout -b feature-ui" -ForegroundColor Yellow
|
||||||
|
$allChecksPassed = $false
|
||||||
|
}
|
||||||
|
|
||||||
|
# Check for merge commits (indicates three-way merge happened)
|
||||||
|
$mergeCommits = git log --merges --oneline 2>$null
|
||||||
|
if ($mergeCommits) {
|
||||||
|
Write-Host "[PASS] Merge commit(s) found (three-way merge completed)" -ForegroundColor Green
|
||||||
|
} else {
|
||||||
|
Write-Host "[FAIL] No merge commits found" -ForegroundColor Red
|
||||||
|
Write-Host "[HINT] Create divergence: make commits on both main and feature-ui, then merge" -ForegroundColor Yellow
|
||||||
|
$allChecksPassed = $false
|
||||||
|
}
|
||||||
|
|
||||||
|
# Check if ui.py exists (should be on main after merge)
|
||||||
|
if (Test-Path "ui.py") {
|
||||||
|
Write-Host "[PASS] File 'ui.py' exists on main (from feature-ui merge)" -ForegroundColor Green
|
||||||
|
} else {
|
||||||
|
Write-Host "[FAIL] File 'ui.py' not found on main" -ForegroundColor Red
|
||||||
|
Write-Host "[HINT] Create ui.py on feature-ui branch and merge it into main" -ForegroundColor Yellow
|
||||||
|
$allChecksPassed = $false
|
||||||
|
}
|
||||||
|
|
||||||
|
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 successfully learned about Git merging!" -ForegroundColor Cyan
|
||||||
|
Write-Host "You now understand:" -ForegroundColor Cyan
|
||||||
|
Write-Host " - Fast-forward merges (when main hasn't changed)" -ForegroundColor White
|
||||||
|
Write-Host " - Three-way merges (when branches have diverged)" -ForegroundColor White
|
||||||
|
Write-Host " - How to use git merge to combine branches" -ForegroundColor White
|
||||||
|
Write-Host " - Merge commits and how to view them" -ForegroundColor White
|
||||||
|
Write-Host "`nReady for the next module!" -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 ""
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user