feat: add initial worktrees example

This commit is contained in:
Bjarke Sporring
2026-01-04 17:53:52 +01:00
parent cde1a6ba70
commit 4b7593b1e7
4 changed files with 533 additions and 0 deletions

View File

@@ -0,0 +1,208 @@
# Module 13: Worktrees
## Learning Objectives
By the end of this module, you will:
- Understand what Git worktrees are and when to use them
- Create and manage multiple working directories for the same repository
- Work on multiple branches simultaneously
- Understand the benefits of worktrees over stashing or cloning
- Remove and clean up worktrees
## Challenge Description
You're working on a feature when an urgent bug report comes in. Instead of stashing your work or creating a separate clone, you'll use Git worktrees to work on both the feature and the bugfix simultaneously in different directories.
Your task is to:
1. Create a worktree for the bugfix on a separate branch
2. Fix the bug in the worktree
3. Commit and verify the fix
4. Continue working on your feature in the main working directory
5. Clean up the worktree when done
## Key Concepts
### What are Git Worktrees?
A worktree is an additional working directory attached to the same repository. Each worktree can have a different branch checked out, allowing you to work on multiple branches simultaneously without switching.
### Traditional Workflow vs Worktrees
**Traditional (switching branches):**
```
main-repo/
- Switch to bugfix branch
- Fix bug
- Switch back to feature branch
- Continue feature work
- (Requires stashing or committing incomplete work)
```
**With Worktrees:**
```
main-repo/ <- feature branch
worktrees/bugfix/ <- bugfix branch
Work in both simultaneously!
```
### Why Use Worktrees?
**Advantages:**
- Work on multiple branches at the same time
- No need to stash or commit incomplete work
- Each worktree has its own working directory and index
- Share the same Git history (one `.git` directory)
- Faster than cloning the entire repository
- Perfect for code reviews, comparisons, or parallel development
**Use Cases:**
- Urgent bug fixes while working on a feature
- Code reviews (checkout PR in separate worktree)
- Comparing implementations side by side
- Running tests on one branch while coding on another
- Building different versions simultaneously
## Useful Commands
```bash
# List all worktrees
git worktree list
# Add a new worktree
git worktree add <path> <branch>
git worktree add ../bugfix bugfix-branch
# Create new branch in worktree
git worktree add <path> -b <new-branch>
git worktree add ../feature-new -b feature-new
# Remove a worktree
git worktree remove <path>
git worktree remove ../bugfix
# Prune stale worktree information
git worktree prune
# Move a worktree
git worktree move <old-path> <new-path>
# Lock a worktree (prevent deletion)
git worktree lock <path>
git worktree unlock <path>
```
## Verification
Run the verification script to check your solution:
```bash
.\verify.ps1
```
The verification will check that:
- You created a worktree for the bugfix
- The bug was fixed and committed in the worktree
- Your feature work continued in the main directory
- Both branches have the expected changes
## Challenge Steps
1. Navigate to the challenge directory
2. You're in main-repo with a feature branch checked out
3. View current worktrees: `git worktree list`
4. Create a worktree for bugfix: `git worktree add ../bugfix-worktree -b bugfix`
5. Navigate to the worktree: `cd ../bugfix-worktree`
6. Fix the bug in calculator.js (fix the divide by zero check)
7. Commit the fix: `git add . && git commit -m "Fix divide by zero bug"`
8. Go back to main repo: `cd ../main-repo`
9. Continue working on your feature
10. Add a new method to calculator.js
11. Commit your feature
12. List worktrees: `git worktree list`
13. Remove the worktree: `git worktree remove ../bugfix-worktree`
14. Run verification
## Tips
- Worktree paths are typically siblings of your main repo (use `../worktree-name`)
- Each worktree must have a different branch checked out
- Can't checkout the same branch in multiple worktrees
- The main `.git` directory is shared, so commits in any worktree are visible everywhere
- Worktrees are listed in `.git/worktrees/`
- Use `git worktree remove` to clean up, or just delete the directory and run `git worktree prune`
- Worktrees persist across restarts until explicitly removed
## Common Worktree Workflows
### Urgent Bugfix
```bash
# Currently on feature branch with uncommitted changes
git worktree add ../hotfix -b hotfix
cd ../hotfix
# Fix the bug
git add .
git commit -m "Fix critical bug"
git push origin hotfix
cd ../main-repo
# Continue working on feature
```
### Code Review
```bash
# Review a pull request without switching branches
git fetch origin pull/123/head:pr-123
git worktree add ../review-pr-123 pr-123
cd ../review-pr-123
# Review code, test it
# Run: npm test, npm start, etc.
cd ../main-repo
git worktree remove ../review-pr-123
```
### Parallel Development
```bash
# Work on two features simultaneously
git worktree add ../feature-a -b feature-a
git worktree add ../feature-b -b feature-b
# Terminal 1
cd feature-a && code .
# Terminal 2
cd feature-b && code .
```
### Build Comparison
```bash
# Compare builds between branches
git worktree add ../release-build release-v2.0
cd ../release-build
npm run build
# Test production build
# Meanwhile, continue development in main repo
```
## Worktree vs Other Approaches
### vs Stashing
- **Stash**: Temporary, one at a time, requires branch switching
- **Worktree**: Persistent, multiple simultaneously, no switching
### vs Cloning
- **Clone**: Full copy, separate `.git`, uses more disk space
- **Worktree**: Shared `.git`, less disk space, instant sync
### vs Branch Switching
- **Switching**: Requires clean working directory, one branch at a time
- **Worktree**: Keep dirty working directory, multiple branches active
## What You'll Learn
Git worktrees are a powerful but underutilized feature that can significantly improve your workflow. They eliminate the need for constant branch switching, stashing, or maintaining multiple clones. Whether you're handling urgent fixes, reviewing code, or comparing implementations, worktrees provide a clean and efficient solution. Once you understand worktrees, you'll find many situations where they're the perfect tool for the job.

View File

@@ -0,0 +1,22 @@
#!/usr/bin/env pwsh
<#
.SYNOPSIS
Resets the worktrees challenge environment.
.DESCRIPTION
Removes the existing challenge directory and runs setup.ps1
to create a fresh challenge environment.
#>
Write-Host "Resetting challenge environment..." -ForegroundColor Yellow
# Remove existing challenge directory if present
if (Test-Path "challenge") {
Remove-Item -Path "challenge" -Recurse -Force
Write-Host "Removed existing challenge directory." -ForegroundColor Cyan
}
# Run setup script
Write-Host "Running setup script...`n" -ForegroundColor Cyan
& ".\setup.ps1"

View File

@@ -0,0 +1,138 @@
#!/usr/bin/env pwsh
<#
.SYNOPSIS
Sets up the worktrees challenge environment.
.DESCRIPTION
Creates a Git repository with a feature in progress, ready for
demonstrating the use of worktrees for parallel work.
#>
# Remove existing challenge directory if present
if (Test-Path "challenge") {
Write-Host "Removing existing challenge directory..." -ForegroundColor Yellow
Remove-Item -Path "challenge" -Recurse -Force
}
# Create challenge directory
Write-Host "Creating challenge environment..." -ForegroundColor Cyan
New-Item -ItemType Directory -Path "challenge" | Out-Null
Set-Location "challenge"
# Create main repository
New-Item -ItemType Directory -Path "main-repo" | Out-Null
Set-Location "main-repo"
# Initialize git repository
git init | Out-Null
git config user.name "Workshop User" | Out-Null
git config user.email "user@workshop.local" | Out-Null
# Create initial calculator with a bug
$calculator = @"
class Calculator {
add(a, b) {
return a + b;
}
subtract(a, b) {
return a - b;
}
multiply(a, b) {
return a * b;
}
// BUG: No division by zero check!
divide(a, b) {
return a / b;
}
}
module.exports = Calculator;
"@
Set-Content -Path "calculator.js" -Value $calculator
git add calculator.js
git commit -m "Initial calculator implementation" | Out-Null
$readme = @"
# Calculator Project
A simple calculator with basic operations.
## Features
- Addition
- Subtraction
- Multiplication
- Division (has a bug!)
"@
Set-Content -Path "README.md" -Value $readme
git add README.md
git commit -m "Add README" | Out-Null
# Create feature branch and start working on it
git checkout -b feature-advanced-math | Out-Null
# Add work in progress on feature branch
$calculatorWithFeature = @"
class Calculator {
add(a, b) {
return a + b;
}
subtract(a, b) {
return a - b;
}
multiply(a, b) {
return a * b;
}
// BUG: No division by zero check!
divide(a, b) {
return a / b;
}
// New feature: power function (work in progress)
power(a, b) {
return Math.pow(a, b);
}
// TODO: Add square root function
// TODO: Add logarithm function
}
module.exports = Calculator;
"@
Set-Content -Path "calculator.js" -Value $calculatorWithFeature
git add calculator.js
git commit -m "Add power function (WIP: more math functions coming)" | Out-Null
# Return to challenge directory
Set-Location ..
Write-Host "`n========================================" -ForegroundColor Green
Write-Host "Challenge environment created!" -ForegroundColor Green
Write-Host "========================================" -ForegroundColor Green
Write-Host "`nSituation:" -ForegroundColor Cyan
Write-Host "You're working on the 'feature-advanced-math' branch" -ForegroundColor White
Write-Host "You have plans to add more math functions (see TODOs)" -ForegroundColor White
Write-Host "`nUrgent: A critical bug was discovered in the divide function!" -ForegroundColor Red
Write-Host "It doesn't check for division by zero." -ForegroundColor Red
Write-Host "`nInstead of stashing your feature work, use a worktree:" -ForegroundColor Yellow
Write-Host "`nYour task:" -ForegroundColor Yellow
Write-Host "1. Navigate to main-repo: cd challenge/main-repo" -ForegroundColor White
Write-Host "2. Create a worktree: git worktree add ../bugfix-worktree -b bugfix" -ForegroundColor White
Write-Host "3. Go to worktree: cd ../bugfix-worktree" -ForegroundColor White
Write-Host "4. Fix the bug in calculator.js:" -ForegroundColor White
Write-Host " Add a check: if (b === 0) throw new Error('Division by zero');" -ForegroundColor White
Write-Host "5. Commit the fix: git add . && git commit -m 'Fix divide by zero bug'" -ForegroundColor White
Write-Host "6. Return to main-repo: cd ../main-repo" -ForegroundColor White
Write-Host "7. Complete your feature: Add square root method to calculator.js" -ForegroundColor White
Write-Host "8. Commit: git add . && git commit -m 'Add square root function'" -ForegroundColor White
Write-Host "9. Clean up worktree: git worktree remove ../bugfix-worktree" -ForegroundColor White
Write-Host "`nRun '../verify.ps1' from the challenge directory to check your solution.`n" -ForegroundColor Cyan

View File

@@ -0,0 +1,165 @@
#!/usr/bin/env pwsh
<#
.SYNOPSIS
Verifies the worktrees challenge solution.
.DESCRIPTION
Checks that the user successfully used worktrees to fix a bug
while continuing work on a feature.
#>
Set-Location "challenge" -ErrorAction SilentlyContinue
# Check if challenge directory exists
if (-not (Test-Path "../verify.ps1")) {
Write-Host "Error: Please run this script from the module directory" -ForegroundColor Red
exit 1
}
if (-not (Test-Path ".")) {
Write-Host "Error: Challenge directory not found. Run setup.ps1 first." -ForegroundColor Red
Set-Location ..
exit 1
}
Write-Host "Verifying your solution..." -ForegroundColor Cyan
# Check if main-repo exists
if (-not (Test-Path "main-repo")) {
Write-Host "[FAIL] main-repo directory not found." -ForegroundColor Red
Set-Location ..
exit 1
}
Set-Location "main-repo"
# Check if it's a git repository
if (-not (Test-Path ".git")) {
Write-Host "[FAIL] main-repo is not a git repository." -ForegroundColor Red
Set-Location ../..
exit 1
}
# Check if bugfix branch exists
$branches = git branch --all 2>$null
if ($branches -notmatch "bugfix") {
Write-Host "[FAIL] bugfix branch not found." -ForegroundColor Red
Write-Host "Hint: Create a worktree with: git worktree add ../bugfix-worktree -b bugfix" -ForegroundColor Yellow
Set-Location ../..
exit 1
}
Write-Host "[PASS] Bugfix branch exists!" -ForegroundColor Green
# Check bugfix branch for the fix
git checkout bugfix 2>$null | Out-Null
if (-not (Test-Path "calculator.js")) {
Write-Host "[FAIL] calculator.js not found on bugfix branch." -ForegroundColor Red
Set-Location ../..
exit 1
}
$bugfixCalc = Get-Content "calculator.js" -Raw
# Check if division by zero check was added
if ($bugfixCalc -notmatch "b === 0|b == 0|division by zero|divide by zero") {
Write-Host "[FAIL] Division by zero check not found in bugfix branch." -ForegroundColor Red
Write-Host "Hint: Add a check in the divide method to prevent division by zero" -ForegroundColor Yellow
Set-Location ../..
exit 1
}
# Check for commit on bugfix branch
$bugfixCommits = git log --pretty=format:"%s" bugfix 2>$null
if ($bugfixCommits -notmatch "bug|fix|division|divide") {
Write-Host "[FAIL] No bugfix commit found on bugfix branch." -ForegroundColor Red
Write-Host "Hint: Commit your fix with a descriptive message" -ForegroundColor Yellow
Set-Location ../..
exit 1
}
Write-Host "[PASS] Bug fixed on bugfix branch!" -ForegroundColor Green
# Check feature branch for continued work
git checkout feature-advanced-math 2>$null | Out-Null
$featureCalc = Get-Content "calculator.js" -Raw
# Check if square root function was added
if ($featureCalc -notmatch "sqrt|squareRoot") {
Write-Host "[FAIL] Square root function not found on feature branch." -ForegroundColor Red
Write-Host "Hint: Add the square root method to calculator.js on feature-advanced-math branch" -ForegroundColor Yellow
Set-Location ../..
exit 1
}
# Check that feature work was committed
$featureCommits = git log --pretty=format:"%s" feature-advanced-math 2>$null
$featureCommitArray = $featureCommits -split "`n"
# Should have at least 3 commits: initial + README + power + sqrt
if ($featureCommitArray.Count -lt 4) {
Write-Host "[FAIL] Not enough commits on feature branch." -ForegroundColor Red
Write-Host "Expected: initial, README, power, and square root commits" -ForegroundColor Yellow
Write-Host "Found $($featureCommitArray.Count) commits" -ForegroundColor Yellow
Set-Location ../..
exit 1
}
# Check if the latest feature commit is about square root
if ($featureCommitArray[0] -notmatch "sqrt|square|root") {
Write-Host "[FAIL] Latest commit on feature branch should be about square root." -ForegroundColor Red
Write-Host "Latest commit: $($featureCommitArray[0])" -ForegroundColor Yellow
Set-Location ../..
exit 1
}
Write-Host "[PASS] Feature work completed!" -ForegroundColor Green
# Check if worktree was cleaned up (bugfix-worktree should not exist or be removed)
Set-Location ..
$worktreeStillExists = Test-Path "bugfix-worktree"
if ($worktreeStillExists) {
Write-Host "[WARNING] bugfix-worktree directory still exists." -ForegroundColor Yellow
Write-Host "Hint: Clean up with: git worktree remove ../bugfix-worktree" -ForegroundColor Yellow
# Don't fail on this, just warn
}
# Check worktree list
Set-Location "main-repo"
$worktrees = git worktree list 2>$null
# Verify that the concept was understood (they should have created the worktree at some point)
# We can check this by looking for the bugfix branch existence
if ($branches -notmatch "bugfix") {
Write-Host "[FAIL] No evidence of worktree usage." -ForegroundColor Red
Set-Location ../..
exit 1
}
# Success!
Write-Host "`n========================================" -ForegroundColor Green
Write-Host "SUCCESS! Challenge completed!" -ForegroundColor Green
Write-Host "========================================" -ForegroundColor Green
Write-Host "`nYou have successfully:" -ForegroundColor Cyan
Write-Host "- Created a worktree for the bugfix" -ForegroundColor White
Write-Host "- Fixed the division by zero bug" -ForegroundColor White
Write-Host "- Committed the fix on the bugfix branch" -ForegroundColor White
Write-Host "- Continued feature work in parallel" -ForegroundColor White
Write-Host "- Added the square root function" -ForegroundColor White
Write-Host "- Committed the feature work" -ForegroundColor White
if (-not $worktreeStillExists) {
Write-Host "- Cleaned up the worktree" -ForegroundColor White
}
Write-Host "`nYou now understand Git worktrees!" -ForegroundColor Green
Write-Host "`nKey takeaway:" -ForegroundColor Yellow
Write-Host "Worktrees let you work on multiple branches simultaneously" -ForegroundColor White
Write-Host "without stashing, switching, or cloning the repository.`n" -ForegroundColor White
Set-Location ../..
exit 0