feat: add reset module 10

This commit is contained in:
Bjarke Sporring
2026-01-04 17:35:25 +01:00
parent 8f488c2e88
commit 515a76dd5a
4 changed files with 617 additions and 0 deletions

View File

@@ -0,0 +1,198 @@
# Module 10: Reset vs Revert
## Learning Objectives
By the end of this module, you will:
- Understand the difference between `git reset` and `git revert`
- Know when to use reset vs revert
- Understand the three modes of reset (--soft, --mixed, --hard)
- Safely undo commits in both local and shared branches
- Understand the risks of rewriting history
## Challenge Description
You have two branches with problematic commits:
1. **local-feature**: A private branch with bad commits that you haven't shared with anyone
2. **shared-feature**: A branch that has been pushed and others might be using
Your task is to:
1. Use `git reset` to remove the bad commit from the local-feature branch (safe because it's not shared)
2. Use `git revert` to undo the bad commit from the shared-feature branch (safe because it preserves history)
## Key Concepts
### Git Reset: Rewriting History
`git reset` moves the branch pointer backward, effectively erasing commits from history. It has three modes:
**--soft**: Moves HEAD, keeps changes staged
```bash
git reset --soft HEAD~1
# Commit is gone, but changes are staged and ready to commit again
```
**--mixed** (default): Moves HEAD, keeps changes unstaged
```bash
git reset HEAD~1
# Commit is gone, changes are in working directory but not staged
```
**--hard**: Moves HEAD, discards all changes
```bash
git reset --hard HEAD~1
# Commit is gone, changes are PERMANENTLY DELETED
```
### Git Revert: Safe Undo
`git revert` creates a NEW commit that undoes the changes from a previous commit. History is preserved.
```bash
git revert <commit-hash>
# Creates a new commit that reverses the specified commit
```
### Visual Comparison
**Before (both branches):**
```
A---B---C---D (D is the bad commit)
```
**After Reset (rewrites history):**
```
A---B---C
```
Commit D is gone. If anyone else had D, they'll have problems.
**After Revert (preserves history):**
```
A---B---C---D---E
```
E is a new commit that undoes D. Everyone can pull E safely.
### When to Use Each
**Use Reset when:**
- The commits haven't been pushed to a shared repository
- You're cleaning up local commits before pushing
- You made a mistake locally and want to start over
- You're working alone on a branch
**Use Revert when:**
- The commits have been pushed to a shared repository
- Others might have based work on these commits
- You want to preserve the complete history
- You need a safe, reversible undo operation
### The Golden Rule
**Never use `git reset` on commits that have been pushed to a shared branch!**
This will cause problems for anyone who has pulled those commits. Use `git revert` instead.
## Useful Commands
```bash
# Reset (for local-only commits)
git reset --soft HEAD~1 # Undo commit, keep changes staged
git reset HEAD~1 # Undo commit, keep changes unstaged
git reset --hard HEAD~1 # Undo commit, discard changes (DANGEROUS!)
# Reset to a specific commit
git reset --hard <commit-hash>
# Revert (for shared commits)
git revert <commit-hash>
git revert HEAD # Revert the last commit
# See what would be affected before resetting
git log --oneline
git diff HEAD~1
# If you reset by mistake, you can sometimes recover with reflog
git reflog
git reset --hard <commit-hash-from-reflog>
```
## Verification
Run the verification script to check your solution:
```bash
.\verify.ps1
```
The verification will check that:
- local-feature branch has the bad commit removed via reset
- shared-feature branch has the bad commit undone via revert
- shared-feature has a revert commit in the history
- All good commits are preserved
## Challenge Steps
1. Navigate to the challenge directory
2. You're on the local-feature branch with a bad commit
3. View commits: `git log --oneline`
4. Use `git reset --hard HEAD~1` to remove the bad commit
5. Switch to shared-feature: `git checkout shared-feature`
6. View commits: `git log --oneline`
7. Find the hash of the "Add broken feature" commit
8. Use `git revert <commit-hash>` to undo it safely
9. Run the verification script
## Tips
- `HEAD~1` means "one commit before HEAD"
- `HEAD~2` means "two commits before HEAD"
- Always check `git log` before and after reset/revert
- `git reset --hard` is DANGEROUS - it permanently deletes uncommitted changes
- If you're unsure, use `git reset --soft` instead of `--hard`
- Revert will open an editor for the commit message - you can accept the default
- You can always use `.\reset.ps1` to start over if you make a mistake
## Common Mistakes to Avoid
### Mistake 1: Using Reset on Pushed Commits
```bash
# DON'T DO THIS if the commit was pushed!
git reset --hard HEAD~1
git push --force # This will cause problems for others
```
### Mistake 2: Using --hard Without Checking
```bash
# This DELETES your work permanently!
git reset --hard HEAD~1 # Uncommitted changes are GONE
```
### Mistake 3: Reverting the Wrong Commit
```bash
# Always double-check the commit hash
git log --oneline
git show <commit-hash> # Verify it's the right commit
git revert <commit-hash> # Now revert it
```
## Recovery from Mistakes
If you reset by accident, Git keeps a reflog:
```bash
# See recent HEAD movements
git reflog
# Find the commit you want to restore
# Output looks like:
# abc1234 HEAD@{0}: reset: moving to HEAD~1
# def5678 HEAD@{1}: commit: The commit you just lost
# Restore it
git reset --hard def5678
```
The reflog is your safety net, but it only keeps history for about 30 days.
## What You'll Learn
Understanding when to use reset versus revert is crucial for safe Git usage. Reset is powerful but dangerous when used on shared commits, while revert is always safe but creates additional history. Mastering both commands and knowing which to use in different situations is a hallmark of Git expertise. The rule is simple: if in doubt, use revert - it's always safe.

View File

@@ -0,0 +1,22 @@
#!/usr/bin/env pwsh
<#
.SYNOPSIS
Resets the reset vs revert 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,225 @@
#!/usr/bin/env pwsh
<#
.SYNOPSIS
Sets up the reset vs revert challenge environment.
.DESCRIPTION
Creates a Git repository with two branches:
- local-feature: A private branch where reset should be used
- shared-feature: A pushed branch where revert should be used
#>
# 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"
# 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 commits on main
$app = @"
class Calculator {
add(a, b) {
return a + b;
}
subtract(a, b) {
return a - b;
}
}
module.exports = Calculator;
"@
Set-Content -Path "calculator.js" -Value $app
git add calculator.js
git commit -m "Initial calculator implementation" | Out-Null
$readme = @"
# Calculator App
A simple calculator application.
"@
Set-Content -Path "README.md" -Value $readme
git add README.md
git commit -m "Add README" | Out-Null
# Create local-feature branch (private, not shared)
git checkout -b local-feature | Out-Null
$appWithMultiply = @"
class Calculator {
add(a, b) {
return a + b;
}
subtract(a, b) {
return a - b;
}
multiply(a, b) {
return a * b;
}
}
module.exports = Calculator;
"@
Set-Content -Path "calculator.js" -Value $appWithMultiply
git add calculator.js
git commit -m "Add multiply function" | Out-Null
# Add a bad commit that should be removed with reset
$appWithBadCode = @"
class Calculator {
add(a, b) {
return a + b;
}
subtract(a, b) {
return a - b;
}
multiply(a, b) {
return a * b;
}
// BUG: This is broken and should never have been committed!
divide(a, b) {
// Forgot to check for division by zero
return a / b; // This will return Infinity or NaN for zero!
}
}
module.exports = Calculator;
"@
Set-Content -Path "calculator.js" -Value $appWithBadCode
git add calculator.js
git commit -m "Add broken divide function - DO NOT KEEP" | Out-Null
# Switch back to main for shared-feature branch
git checkout main | Out-Null
# Create shared-feature branch (simulating a pushed/shared branch)
git checkout -b shared-feature | Out-Null
$appWithPower = @"
class Calculator {
add(a, b) {
return a + b;
}
subtract(a, b) {
return a - b;
}
power(a, b) {
return Math.pow(a, b);
}
}
module.exports = Calculator;
"@
Set-Content -Path "calculator.js" -Value $appWithPower
git add calculator.js
git commit -m "Add power function" | Out-Null
# Add a bad commit that should be reverted (not reset)
$appWithBrokenFeature = @"
class Calculator {
add(a, b) {
return a + b;
}
subtract(a, b) {
return a - b;
}
power(a, b) {
return Math.pow(a, b);
}
// BUG: This breaks the calculator!
squareRoot(a) {
// This implementation is wrong for negative numbers
return Math.sqrt(a); // Returns NaN for negative numbers without warning!
}
}
module.exports = Calculator;
"@
Set-Content -Path "calculator.js" -Value $appWithBrokenFeature
git add calculator.js
git commit -m "Add broken feature" | Out-Null
# Add another good commit after the bad one (to show that revert preserves subsequent commits)
$appWithMoreFeatures = @"
class Calculator {
add(a, b) {
return a + b;
}
subtract(a, b) {
return a - b;
}
power(a, b) {
return Math.pow(a, b);
}
// BUG: This breaks the calculator!
squareRoot(a) {
// This implementation is wrong for negative numbers
return Math.sqrt(a); // Returns NaN for negative numbers without warning!
}
modulo(a, b) {
return a % b;
}
}
module.exports = Calculator;
"@
Set-Content -Path "calculator.js" -Value $appWithMoreFeatures
git add calculator.js
git commit -m "Add modulo function" | Out-Null
# Switch to local-feature for the challenge start
git checkout local-feature | Out-Null
# Return to module directory
Set-Location ..
Write-Host "`n========================================" -ForegroundColor Green
Write-Host "Challenge environment created!" -ForegroundColor Green
Write-Host "========================================" -ForegroundColor Green
Write-Host "`nYou have two branches with bad commits:" -ForegroundColor Cyan
Write-Host "`n1. local-feature (PRIVATE - not shared):" -ForegroundColor Yellow
Write-Host " - Has a broken divide function commit" -ForegroundColor White
Write-Host " - Safe to use 'git reset' to remove it" -ForegroundColor Green
Write-Host "`n2. shared-feature (PUBLIC - shared with team):" -ForegroundColor Yellow
Write-Host " - Has a broken feature commit" -ForegroundColor White
Write-Host " - Must use 'git revert' to undo it safely" -ForegroundColor Green
Write-Host "`nYour task:" -ForegroundColor Yellow
Write-Host "1. Navigate to the challenge directory: cd challenge" -ForegroundColor White
Write-Host "2. You're on local-feature - view commits: git log --oneline" -ForegroundColor White
Write-Host "3. Remove the bad commit with: git reset --hard HEAD~1" -ForegroundColor White
Write-Host "4. Switch to shared-feature: git checkout shared-feature" -ForegroundColor White
Write-Host "5. Find the 'Add broken feature' commit hash: git log --oneline" -ForegroundColor White
Write-Host "6. Revert it with: git revert <commit-hash>" -ForegroundColor White
Write-Host "`nRun '../verify.ps1' from the challenge directory to check your solution.`n" -ForegroundColor Cyan

View File

@@ -0,0 +1,172 @@
#!/usr/bin/env pwsh
<#
.SYNOPSIS
Verifies the reset vs revert challenge solution.
.DESCRIPTION
Checks that the user correctly used reset on the local branch
and revert on the shared branch.
#>
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 git repository exists
if (-not (Test-Path ".git")) {
Write-Host "[FAIL] No git repository found." -ForegroundColor Red
Set-Location ..
exit 1
}
# Verify local-feature branch
Write-Host "`nChecking local-feature branch..." -ForegroundColor Cyan
git checkout local-feature 2>$null | Out-Null
# Check commit count on local-feature (should be 3: initial + README + multiply)
$localCommitCount = (git rev-list --count local-feature 2>$null)
if ($localCommitCount -ne 3) {
Write-Host "[FAIL] local-feature should have 3 commits, found $localCommitCount" -ForegroundColor Red
if ($localCommitCount -gt 3) {
Write-Host "Hint: The bad commit should be removed. Use 'git reset --hard HEAD~1'" -ForegroundColor Yellow
} else {
Write-Host "Hint: You may have reset too far. Run ../reset.ps1 to start over." -ForegroundColor Yellow
}
Set-Location ..
exit 1
}
# Check that calculator.js exists
if (-not (Test-Path "calculator.js")) {
Write-Host "[FAIL] calculator.js not found." -ForegroundColor Red
Set-Location ..
exit 1
}
# Check calculator.js on local-feature
$localCalcContent = Get-Content "calculator.js" -Raw
# Should have multiply function
if ($localCalcContent -notmatch "multiply") {
Write-Host "[FAIL] calculator.js should have the multiply function." -ForegroundColor Red
Set-Location ..
exit 1
}
# Should NOT have divide function (it was in the bad commit that should be reset)
if ($localCalcContent -match "divide") {
Write-Host "[FAIL] calculator.js should NOT have the divide function." -ForegroundColor Red
Write-Host "Hint: Use 'git reset --hard HEAD~1' to remove the bad commit" -ForegroundColor Yellow
Set-Location ..
exit 1
}
# Check commit messages on local-feature
$localCommits = git log --pretty=format:"%s" local-feature 2>$null
if ($localCommits -match "broken divide") {
Write-Host "[FAIL] The 'broken divide' commit should be removed from local-feature." -ForegroundColor Red
Write-Host "Hint: Use 'git reset --hard HEAD~1' to remove it" -ForegroundColor Yellow
Set-Location ..
exit 1
}
Write-Host "[PASS] local-feature branch correctly reset!" -ForegroundColor Green
# Verify shared-feature branch
Write-Host "`nChecking shared-feature branch..." -ForegroundColor Cyan
git checkout shared-feature 2>$null | Out-Null
# Check commit count on shared-feature
# Should be 6: initial + README + power + broken feature + modulo + revert
$sharedCommitCount = (git rev-list --count shared-feature 2>$null)
if ($sharedCommitCount -ne 6) {
Write-Host "[FAIL] shared-feature should have 6 commits, found $sharedCommitCount" -ForegroundColor Red
if ($sharedCommitCount -lt 6) {
Write-Host "Hint: You should REVERT the bad commit, not reset it." -ForegroundColor Yellow
Write-Host " Revert creates a new commit that undoes the bad one." -ForegroundColor Yellow
Write-Host " Use: git revert <commit-hash>" -ForegroundColor Yellow
} else {
Write-Host "Hint: You should have exactly 6 commits after reverting." -ForegroundColor Yellow
}
Set-Location ..
exit 1
}
# Check that there's a revert commit
$sharedCommits = git log --pretty=format:"%s" shared-feature 2>$null
if ($sharedCommits -notmatch "Revert") {
Write-Host "[FAIL] No revert commit found on shared-feature." -ForegroundColor Red
Write-Host "Hint: Use 'git revert <commit-hash>' to undo the bad commit" -ForegroundColor Yellow
Write-Host " Find the hash with: git log --oneline" -ForegroundColor Yellow
Set-Location ..
exit 1
}
# Check calculator.js on shared-feature
$sharedCalcContent = Get-Content "calculator.js" -Raw
# Should have power function
if ($sharedCalcContent -notmatch "power") {
Write-Host "[FAIL] calculator.js should have the power function." -ForegroundColor Red
Set-Location ..
exit 1
}
# Should have modulo function (commits after the reverted one should be preserved)
if ($sharedCalcContent -notmatch "modulo") {
Write-Host "[FAIL] calculator.js should have the modulo function." -ForegroundColor Red
Write-Host "Hint: Reverting should preserve commits made after the bad one" -ForegroundColor Yellow
Set-Location ..
exit 1
}
# Should NOT have squareRoot function (it was in the bad commit that should be reverted)
if ($sharedCalcContent -match "squareRoot") {
Write-Host "[FAIL] calculator.js should NOT have the squareRoot function." -ForegroundColor Red
Write-Host "Hint: The 'Add broken feature' commit should be reverted" -ForegroundColor Yellow
Write-Host " Use: git revert <commit-hash-of-broken-feature>" -ForegroundColor Yellow
Set-Location ..
exit 1
}
# Verify the revert commit specifically reverted the "Add broken feature" commit
$revertCommitMessage = git log --grep="Revert" --pretty=format:"%s" -n 1 2>$null
if ($revertCommitMessage -notmatch "broken feature") {
Write-Host "[FAIL] The revert commit should mention 'broken feature'." -ForegroundColor Red
Write-Host "Hint: Make sure you reverted the correct commit (the one that added squareRoot)" -ForegroundColor Yellow
Set-Location ..
exit 1
}
Write-Host "[PASS] shared-feature branch correctly reverted!" -ForegroundColor Green
# 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 "- Used 'git reset' on local-feature (private branch)" -ForegroundColor White
Write-Host " Removed the bad commit completely from history" -ForegroundColor White
Write-Host "- Used 'git revert' on shared-feature (public branch)" -ForegroundColor White
Write-Host " Created a new commit that undoes the bad one" -ForegroundColor White
Write-Host " Preserved all history and subsequent commits" -ForegroundColor White
Write-Host "`nYou now understand when to use reset vs revert!" -ForegroundColor Green
Write-Host "`nKey takeaway:" -ForegroundColor Yellow
Write-Host "- Reset rewrites history (use only on private commits)" -ForegroundColor White
Write-Host "- Revert preserves history (safe for shared commits)`n" -ForegroundColor White
Set-Location ..
exit 0