feat: add remotes and stash
This commit is contained in:
199
module-10-stash/README.md
Normal file
199
module-10-stash/README.md
Normal file
@@ -0,0 +1,199 @@
|
||||
# Module 11: Stash
|
||||
|
||||
## Learning Objectives
|
||||
|
||||
By the end of this module, you will:
|
||||
- Understand what git stash is and when to use it
|
||||
- Temporarily save work without committing
|
||||
- Switch between branches without losing uncommitted changes
|
||||
- Manage multiple stashes
|
||||
- Apply and remove stashed changes
|
||||
- Understand the difference between stash pop and stash apply
|
||||
|
||||
## Challenge Description
|
||||
|
||||
You're working on a feature when your teammate reports a critical bug in production. You need to switch to the main branch to fix it immediately, but your current work is incomplete and not ready to commit.
|
||||
|
||||
Your task is to:
|
||||
1. Stash your incomplete work
|
||||
2. Switch to the main branch
|
||||
3. Fix the urgent bug and commit it
|
||||
4. Return to your feature branch
|
||||
5. Restore your stashed changes
|
||||
6. Complete your feature and commit it
|
||||
|
||||
## Key Concepts
|
||||
|
||||
### What is Git Stash?
|
||||
|
||||
Git stash temporarily saves your uncommitted changes (both staged and unstaged) and reverts your working directory to match the HEAD commit. Think of it as a clipboard for your changes.
|
||||
|
||||
### When to Use Stash
|
||||
|
||||
Use stash when you need to:
|
||||
- Switch branches but have uncommitted changes
|
||||
- Pull updates from remote but have local modifications
|
||||
- Quickly test something on a clean working directory
|
||||
- Save work temporarily without creating a commit
|
||||
- Context-switch between tasks
|
||||
|
||||
### Stash vs Commit
|
||||
|
||||
**Stash:**
|
||||
- Temporary storage
|
||||
- Not part of project history
|
||||
- Can be applied to different branches
|
||||
- Easy to discard if not needed
|
||||
- Local only (not pushed to remote)
|
||||
|
||||
**Commit:**
|
||||
- Permanent part of history
|
||||
- Creates a snapshot in the project timeline
|
||||
- Associated with a specific branch
|
||||
- Should be meaningful and complete
|
||||
- Can be pushed to remote
|
||||
|
||||
### The Stash Stack
|
||||
|
||||
Git stash works like a stack (LIFO - Last In, First Out):
|
||||
```
|
||||
stash@{0} <- Most recent stash (top of stack)
|
||||
stash@{1}
|
||||
stash@{2} <- Oldest stash
|
||||
```
|
||||
|
||||
You can have multiple stashes and apply any of them.
|
||||
|
||||
## Useful Commands
|
||||
|
||||
```bash
|
||||
# Stash current changes
|
||||
git stash
|
||||
git stash save "description" # With a descriptive message
|
||||
|
||||
# Stash including untracked files
|
||||
git stash -u
|
||||
|
||||
# List all stashes
|
||||
git stash list
|
||||
|
||||
# Apply most recent stash and remove it from stack
|
||||
git stash pop
|
||||
|
||||
# Apply most recent stash but keep it in stack
|
||||
git stash apply
|
||||
|
||||
# Apply a specific stash
|
||||
git stash apply stash@{1}
|
||||
|
||||
# Show what's in a stash
|
||||
git stash show
|
||||
git stash show -p # Show full diff
|
||||
|
||||
# Drop (delete) a stash
|
||||
git stash drop stash@{0}
|
||||
|
||||
# Clear all stashes
|
||||
git stash clear
|
||||
|
||||
# Create a branch from a stash
|
||||
git stash branch new-branch-name
|
||||
```
|
||||
|
||||
## Verification
|
||||
|
||||
Run the verification script to check your solution:
|
||||
|
||||
```bash
|
||||
.\verify.ps1
|
||||
```
|
||||
|
||||
The verification will check that:
|
||||
- The bug fix commit exists on main
|
||||
- Your feature is completed on the feature branch
|
||||
- Changes were properly stashed and restored
|
||||
- No uncommitted changes remain
|
||||
|
||||
## Challenge Steps
|
||||
|
||||
1. Navigate to the challenge directory
|
||||
2. You're on feature-login with uncommitted changes
|
||||
3. Check status: `git status` (you'll see modified files)
|
||||
4. Stash your changes: `git stash save "WIP: login feature"`
|
||||
5. Verify working directory is clean: `git status`
|
||||
6. Switch to main: `git checkout main`
|
||||
7. View the bug in app.js and fix it (remove the incorrect line)
|
||||
8. Commit the fix: `git add app.js && git commit -m "Fix critical security bug"`
|
||||
9. Switch back to feature: `git checkout feature-login`
|
||||
10. Restore your work: `git stash pop`
|
||||
11. Complete the feature (the TODOs in login.js)
|
||||
12. Commit your completed feature
|
||||
13. Run verification
|
||||
|
||||
## Tips
|
||||
|
||||
- Always use `git stash save "message"` to describe what you're stashing
|
||||
- Use `git stash list` to see all your stashes
|
||||
- `git stash pop` applies and removes the stash (use this most often)
|
||||
- `git stash apply` keeps the stash (useful if you want to apply it to multiple branches)
|
||||
- Stashes are local - they don't get pushed to remote repositories
|
||||
- You can stash even if you have changes to different files
|
||||
- Stash before pulling to avoid merge conflicts
|
||||
- Use `git stash show -p` to preview what's in a stash before applying
|
||||
|
||||
## Common Stash Scenarios
|
||||
|
||||
### Scenario 1: Quick Branch Switch
|
||||
```bash
|
||||
# Working on feature, need to switch to main
|
||||
git stash
|
||||
git checkout main
|
||||
# Do work on main
|
||||
git checkout feature
|
||||
git stash pop
|
||||
```
|
||||
|
||||
### Scenario 2: Pull with Local Changes
|
||||
```bash
|
||||
# You have local changes but need to pull
|
||||
git stash
|
||||
git pull
|
||||
git stash pop
|
||||
# Resolve any conflicts
|
||||
```
|
||||
|
||||
### Scenario 3: Experimental Changes
|
||||
```bash
|
||||
# Try something experimental
|
||||
git stash # Save current work
|
||||
# Make experimental changes
|
||||
# Decide you don't like it
|
||||
git restore . # Discard experiment
|
||||
git stash pop # Restore original work
|
||||
```
|
||||
|
||||
### Scenario 4: Apply to Multiple Branches
|
||||
```bash
|
||||
# Same fix needed on multiple branches
|
||||
git stash
|
||||
git checkout branch1
|
||||
git stash apply
|
||||
git commit -am "Apply fix"
|
||||
git checkout branch2
|
||||
git stash apply
|
||||
git commit -am "Apply fix"
|
||||
git stash drop # Clean up when done
|
||||
```
|
||||
|
||||
## Stash Conflicts
|
||||
|
||||
If applying a stash causes conflicts:
|
||||
1. Git will mark the conflicts in your files
|
||||
2. Resolve conflicts manually (like merge conflicts)
|
||||
3. Stage the resolved files: `git add <file>`
|
||||
4. The stash is automatically dropped after successful pop
|
||||
5. If you used `apply`, manually drop it: `git stash drop`
|
||||
|
||||
## What You'll Learn
|
||||
|
||||
Git stash is an essential tool for managing context switches in your daily workflow. It lets you maintain a clean working directory while preserving incomplete work, making it easy to handle interruptions, urgent fixes, and quick branch switches. Mastering stash makes you more efficient and helps avoid the temptation to make "WIP" commits just to switch branches. Think of stash as your temporary workspace that follows you around.
|
||||
22
module-10-stash/reset.ps1
Normal file
22
module-10-stash/reset.ps1
Normal file
@@ -0,0 +1,22 @@
|
||||
#!/usr/bin/env pwsh
|
||||
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Resets the stash 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"
|
||||
178
module-10-stash/setup.ps1
Normal file
178
module-10-stash/setup.ps1
Normal file
@@ -0,0 +1,178 @@
|
||||
#!/usr/bin/env pwsh
|
||||
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Sets up the stash challenge environment.
|
||||
|
||||
.DESCRIPTION
|
||||
Creates a Git repository with work in progress that needs to be stashed
|
||||
while handling an urgent bug fix.
|
||||
#>
|
||||
|
||||
# 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 application on main
|
||||
$app = @"
|
||||
class Application {
|
||||
constructor() {
|
||||
this.name = 'MyApp';
|
||||
this.version = '1.0.0';
|
||||
}
|
||||
|
||||
start() {
|
||||
console.log('Application started');
|
||||
this.authenticate();
|
||||
}
|
||||
|
||||
authenticate() {
|
||||
console.log('Authentication check');
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Application;
|
||||
"@
|
||||
|
||||
Set-Content -Path "app.js" -Value $app
|
||||
git add app.js
|
||||
git commit -m "Initial application" | Out-Null
|
||||
|
||||
$readme = @"
|
||||
# MyApp
|
||||
|
||||
A sample application for learning Git stash.
|
||||
"@
|
||||
|
||||
Set-Content -Path "README.md" -Value $readme
|
||||
git add README.md
|
||||
git commit -m "Add README" | Out-Null
|
||||
|
||||
# Create feature branch for login feature
|
||||
git checkout -b feature-login | Out-Null
|
||||
|
||||
# Start working on login feature
|
||||
$loginInitial = @"
|
||||
class LoginService {
|
||||
constructor() {
|
||||
this.users = new Map();
|
||||
}
|
||||
|
||||
register(username, password) {
|
||||
if (this.users.has(username)) {
|
||||
throw new Error('User already exists');
|
||||
}
|
||||
this.users.set(username, { password, createdAt: new Date() });
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = LoginService;
|
||||
"@
|
||||
|
||||
Set-Content -Path "login.js" -Value $loginInitial
|
||||
git add login.js
|
||||
git commit -m "Start login service implementation" | Out-Null
|
||||
|
||||
# Add a critical bug to main branch (simulating a bug that was introduced)
|
||||
git checkout main | Out-Null
|
||||
|
||||
$appWithBug = @"
|
||||
class Application {
|
||||
constructor() {
|
||||
this.name = 'MyApp';
|
||||
this.version = '1.0.0';
|
||||
}
|
||||
|
||||
start() {
|
||||
console.log('Application started');
|
||||
this.authenticate();
|
||||
}
|
||||
|
||||
authenticate() {
|
||||
console.log('Authentication check');
|
||||
// BUG: This allows unauthenticated access!
|
||||
return true; // Should check actual credentials
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Application;
|
||||
"@
|
||||
|
||||
Set-Content -Path "app.js" -Value $appWithBug
|
||||
git add app.js
|
||||
git commit -m "Update authentication (contains bug)" | Out-Null
|
||||
|
||||
# Go back to feature branch
|
||||
git checkout feature-login | Out-Null
|
||||
|
||||
# Create work in progress (uncommitted changes)
|
||||
$loginWIP = @"
|
||||
class LoginService {
|
||||
constructor() {
|
||||
this.users = new Map();
|
||||
}
|
||||
|
||||
register(username, password) {
|
||||
if (this.users.has(username)) {
|
||||
throw new Error('User already exists');
|
||||
}
|
||||
this.users.set(username, { password, createdAt: new Date() });
|
||||
return true;
|
||||
}
|
||||
|
||||
// TODO: Complete this method
|
||||
login(username, password) {
|
||||
if (!this.users.has(username)) {
|
||||
throw new Error('User not found');
|
||||
}
|
||||
// TODO: Verify password
|
||||
// TODO: Return user session
|
||||
}
|
||||
|
||||
// TODO: Add logout method
|
||||
}
|
||||
|
||||
module.exports = LoginService;
|
||||
"@
|
||||
|
||||
Set-Content -Path "login.js" -Value $loginWIP
|
||||
|
||||
# Don't commit - leave as uncommitted changes
|
||||
|
||||
# Return to module 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 login feature (feature-login branch)" -ForegroundColor White
|
||||
Write-Host "You have uncommitted changes - the feature is NOT complete yet" -ForegroundColor Yellow
|
||||
Write-Host "`nUrgent: A critical security bug was found in production (main branch)!" -ForegroundColor Red
|
||||
Write-Host "You need to fix it immediately, but your current work isn't ready to commit." -ForegroundColor Red
|
||||
Write-Host "`nYour task:" -ForegroundColor Yellow
|
||||
Write-Host "1. Navigate to the challenge directory: cd challenge" -ForegroundColor White
|
||||
Write-Host "2. Check your status: git status (see uncommitted changes)" -ForegroundColor White
|
||||
Write-Host "3. Stash your work: git stash save 'WIP: login feature'" -ForegroundColor White
|
||||
Write-Host "4. Switch to main: git checkout main" -ForegroundColor White
|
||||
Write-Host "5. Fix the security bug in app.js (remove the comment and fix the auth)" -ForegroundColor White
|
||||
Write-Host "6. Commit the fix: git add app.js && git commit -m 'Fix critical security bug'" -ForegroundColor White
|
||||
Write-Host "7. Switch back: git checkout feature-login" -ForegroundColor White
|
||||
Write-Host "8. Restore your work: git stash pop" -ForegroundColor White
|
||||
Write-Host "9. Complete the TODOs in login.js" -ForegroundColor White
|
||||
Write-Host "10. Commit your completed feature" -ForegroundColor White
|
||||
Write-Host "`nRun '../verify.ps1' from the challenge directory to check your solution.`n" -ForegroundColor Cyan
|
||||
180
module-10-stash/verify.ps1
Normal file
180
module-10-stash/verify.ps1
Normal file
@@ -0,0 +1,180 @@
|
||||
#!/usr/bin/env pwsh
|
||||
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Verifies the stash challenge solution.
|
||||
|
||||
.DESCRIPTION
|
||||
Checks that the user successfully stashed work, fixed the bug on main,
|
||||
and completed the feature on the feature 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
|
||||
}
|
||||
|
||||
# Check current branch
|
||||
$currentBranch = git branch --show-current 2>$null
|
||||
if ($currentBranch -ne "feature-login") {
|
||||
Write-Host "[FAIL] You should be on the 'feature-login' branch." -ForegroundColor Red
|
||||
Write-Host "Current branch: $currentBranch" -ForegroundColor Yellow
|
||||
Write-Host "Hint: Switch back to feature-login after completing the challenge" -ForegroundColor Yellow
|
||||
Set-Location ..
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Check for uncommitted changes on feature-login
|
||||
$status = git status --porcelain 2>$null
|
||||
if ($status) {
|
||||
Write-Host "[FAIL] You have uncommitted changes on feature-login." -ForegroundColor Red
|
||||
Write-Host "Hint: After restoring from stash, you should complete and commit the feature" -ForegroundColor Yellow
|
||||
git status --short
|
||||
Set-Location ..
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Verify main branch has the security fix
|
||||
Write-Host "`nChecking main branch for bug fix..." -ForegroundColor Cyan
|
||||
git checkout main 2>$null | Out-Null
|
||||
|
||||
# Check for bug fix commit
|
||||
$mainCommits = git log --pretty=format:"%s" main 2>$null
|
||||
if ($mainCommits -notmatch "security bug|Fix.*bug|security fix") {
|
||||
Write-Host "[FAIL] No security bug fix commit found on main branch." -ForegroundColor Red
|
||||
Write-Host "Hint: After stashing, switch to main and commit a bug fix" -ForegroundColor Yellow
|
||||
git checkout feature-login 2>$null | Out-Null
|
||||
Set-Location ..
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Check that app.js has been fixed
|
||||
if (-not (Test-Path "app.js")) {
|
||||
Write-Host "[FAIL] app.js not found on main branch." -ForegroundColor Red
|
||||
git checkout feature-login 2>$null | Out-Null
|
||||
Set-Location ..
|
||||
exit 1
|
||||
}
|
||||
|
||||
$appContent = Get-Content "app.js" -Raw
|
||||
|
||||
# The bug was "return true" in authenticate - it should be fixed now
|
||||
# We'll check that the buggy comment is gone or the implementation is improved
|
||||
if ($appContent -match "allows unauthenticated access") {
|
||||
Write-Host "[FAIL] The security bug comment still exists in app.js." -ForegroundColor Red
|
||||
Write-Host "Hint: Remove the bug from app.js and commit the fix" -ForegroundColor Yellow
|
||||
git checkout feature-login 2>$null | Out-Null
|
||||
Set-Location ..
|
||||
exit 1
|
||||
}
|
||||
|
||||
Write-Host "[PASS] Security bug fixed on main!" -ForegroundColor Green
|
||||
|
||||
# Switch back to feature-login
|
||||
Write-Host "`nChecking feature-login branch..." -ForegroundColor Cyan
|
||||
git checkout feature-login 2>$null | Out-Null
|
||||
|
||||
# Check for completed feature commit
|
||||
$featureCommits = git log --pretty=format:"%s" feature-login 2>$null
|
||||
$commitCount = ($featureCommits -split "`n").Count
|
||||
|
||||
if ($commitCount -lt 3) {
|
||||
Write-Host "[FAIL] Expected at least 3 commits on feature-login." -ForegroundColor Red
|
||||
Write-Host "Hint: You should have the initial commits plus your completed feature commit" -ForegroundColor Yellow
|
||||
Set-Location ..
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Check that login.js exists
|
||||
if (-not (Test-Path "login.js")) {
|
||||
Write-Host "[FAIL] login.js not found on feature-login branch." -ForegroundColor Red
|
||||
Set-Location ..
|
||||
exit 1
|
||||
}
|
||||
|
||||
$loginContent = Get-Content "login.js" -Raw
|
||||
|
||||
# Check that login method exists and is implemented
|
||||
if ($loginContent -notmatch "login\(username, password\)") {
|
||||
Write-Host "[FAIL] login.js should have a login method." -ForegroundColor Red
|
||||
Set-Location ..
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Check that TODOs are completed (no TODO comments should remain)
|
||||
if ($loginContent -match "TODO") {
|
||||
Write-Host "[FAIL] login.js still contains TODO comments." -ForegroundColor Red
|
||||
Write-Host "Hint: Complete all the TODOs in login.js before committing" -ForegroundColor Yellow
|
||||
Set-Location ..
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Check that password verification is implemented
|
||||
if ($loginContent -notmatch "password") {
|
||||
Write-Host "[FAIL] login method should verify the password." -ForegroundColor Red
|
||||
Set-Location ..
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Check that the feature has been committed (not just in working directory)
|
||||
$lastCommit = git log -1 --pretty=format:"%s" 2>$null
|
||||
if ($lastCommit -notmatch "login|feature|complete|implement") {
|
||||
Write-Host "[FAIL] Your completed feature should be committed." -ForegroundColor Red
|
||||
Write-Host "Last commit: $lastCommit" -ForegroundColor Yellow
|
||||
Write-Host "Hint: After popping the stash and completing the TODOs, commit the feature" -ForegroundColor Yellow
|
||||
Set-Location ..
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Check that no stashes remain (optional check - they should have used pop)
|
||||
$stashCount = (git stash list 2>$null | Measure-Object).Count
|
||||
if ($stashCount -gt 0) {
|
||||
Write-Host "[WARNING] You have $stashCount stash(es) remaining." -ForegroundColor Yellow
|
||||
Write-Host "Hint: Use 'git stash pop' instead of 'git stash apply' to automatically remove the stash" -ForegroundColor Yellow
|
||||
Write-Host " Or clean up with 'git stash drop'" -ForegroundColor Yellow
|
||||
# Don't fail on this, just warn
|
||||
}
|
||||
|
||||
# Verify the login implementation is complete
|
||||
if ($loginContent -notmatch "logout|session") {
|
||||
Write-Host "[PARTIAL] login.js is missing logout or session functionality." -ForegroundColor Yellow
|
||||
Write-Host "Consider adding logout method for a complete implementation" -ForegroundColor Yellow
|
||||
# Don't fail - this is just a suggestion
|
||||
}
|
||||
|
||||
# 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 "- Stashed your work in progress" -ForegroundColor White
|
||||
Write-Host "- Switched to main branch cleanly" -ForegroundColor White
|
||||
Write-Host "- Fixed the critical security bug" -ForegroundColor White
|
||||
Write-Host "- Returned to your feature branch" -ForegroundColor White
|
||||
Write-Host "- Restored your stashed work" -ForegroundColor White
|
||||
Write-Host "- Completed and committed the login feature" -ForegroundColor White
|
||||
Write-Host "`nYou now understand how to use git stash!" -ForegroundColor Green
|
||||
Write-Host "`nKey takeaway:" -ForegroundColor Yellow
|
||||
Write-Host "Stash lets you save incomplete work without committing," -ForegroundColor White
|
||||
Write-Host "making it easy to context-switch and handle interruptions.`n" -ForegroundColor White
|
||||
|
||||
Set-Location ..
|
||||
exit 0
|
||||
Reference in New Issue
Block a user