feat: add remotes and stash

This commit is contained in:
Bjarke Sporring
2026-01-04 17:53:41 +01:00
parent 515a76dd5a
commit cde1a6ba70
32 changed files with 1178 additions and 379 deletions

View File

@@ -1,106 +0,0 @@
# Module 05: Merge Conflicts
## Learning Objectives
In this module, you will:
- Understand what merge conflicts are and why they occur
- Recognize conflict markers in files
- Manually resolve merge conflicts
- Complete a merge after resolving conflicts
- Use `git status` to identify conflicted files
## 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 conflicting changes on two branches.
### Your Task
You will intentionally create and resolve a merge conflict - a crucial skill for collaborative development!
**The Scenario:**
- The `main` branch and `feature-updates` branch both modified the same lines in `app.py`
- When you try to merge them, Git can't automatically resolve the differences
- You must manually resolve the conflict
**Steps:**
1. Navigate to the challenge directory: `cd challenge`
2. Verify you're on main: `git branch`
3. Attempt to merge feature-updates: `git merge feature-updates`
4. Git will report a conflict! Don't panic - this is expected
5. Check status: `git status` (shows conflicted files)
6. Open `app.py` in a text editor
7. Look for conflict markers:
```
<<<<<<< HEAD
(your current branch's version)
=======
(the other branch's version)
>>>>>>> feature-updates
```
8. Manually resolve by editing the file:
- Remove the conflict markers (`<<<<<<<`, `=======`, `>>>>>>>`)
- Keep the version you want, or combine both changes
9. Save the file
10. Mark as resolved: `git add app.py`
11. Complete the merge: `git commit`
12. View the result: `git log --oneline --graph`
> **Important Notes:**
> - Conflicts occur when the **same lines** are changed differently in both branches
> - Conflict markers show both versions - you choose what to keep
> - You can keep one version, the other, or combine them creatively
> - After editing, you MUST `git add` to mark the conflict as resolved
> - Then `git commit` to complete the merge
> - Use `git merge --abort` if you want to cancel and start over
## Key Concepts
- **Merge Conflict**: Occurs when the same lines in a file are changed differently in both branches being merged.
- **Conflict Markers**: Special markers Git inserts to show the conflicting versions:
- `<<<<<<< HEAD`: Marks the start of your current branch's changes
- `=======`: Separates the two conflicting versions
- `>>>>>>> branch-name`: Marks the end of the other branch's changes
- **Conflict Resolution**: The process of manually editing the file to resolve the differences and remove conflict markers.
- **Staging Resolution**: Using `git add` to tell Git you've resolved the conflict.
## Useful Commands
```bash
git merge <branch> # Attempt merge (may cause conflict)
git status # See which files have conflicts
git diff # View conflicts in detail
cat <file> # View file with conflict markers
git add <file> # Mark conflict as resolved
git commit # Complete the merge
git commit -m "message" # Complete merge with message
git merge --abort # Cancel merge and go back
```
## Verification
Once you've resolved the conflict and completed the merge, verify your solution:
```powershell
.\verify.ps1
```
The verification script will check that you've successfully resolved the conflict and completed the merge.
## 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.

View File

@@ -1,26 +0,0 @@
#!/usr/bin/env pwsh
<#
.SYNOPSIS
Resets the Module 05 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 05 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"

View File

@@ -1,124 +0,0 @@
#!/usr/bin/env pwsh
<#
.SYNOPSIS
Sets up the Module 05 challenge environment for learning about merge conflicts.
.DESCRIPTION
This script creates a challenge directory with a Git repository that
contains conflicting changes on two branches. Students will practice
resolving merge conflicts.
#>
Write-Host "`n=== Setting up Module 05 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"
# Create initial app.py with clear line numbers
Write-Host "Creating initial application..." -ForegroundColor Green
$initialContent = @"
# app.py - Main application
def main():
print("Application starting...")
# Lines 5-7 will be modified differently on each branch
print("Version: 1.0")
print("Status: Development")
print("Mode: Standard")
# End of conflicting section
print("Application ready!")
if __name__ == "__main__":
main()
"@
Set-Content -Path "app.py" -Value $initialContent
git add .
git commit -m "Initial application setup" | Out-Null
# Create feature-updates branch
Write-Host "Creating feature-updates branch..." -ForegroundColor Green
git checkout -b feature-updates 2>$null | Out-Null
# On feature branch: Modify lines 5-7 (Version 2.0, Beta, Advanced)
$featureContent = @"
# app.py - Main application
def main():
print("Application starting...")
# Lines 5-7 will be modified differently on each branch
print("Version: 2.0")
print("Status: Beta")
print("Mode: Advanced")
# End of conflicting section
print("Application ready!")
if __name__ == "__main__":
main()
"@
Set-Content -Path "app.py" -Value $featureContent
git add .
git commit -m "Update to version 2.0 with advanced features" | Out-Null
# Switch back to main
Write-Host "Switching back to main branch..." -ForegroundColor Green
git checkout main 2>$null | Out-Null
# On main: Modify THE SAME lines 5-7 differently (Version 1.5, Production, Basic)
$mainContent = @"
# app.py - Main application
def main():
print("Application starting...")
# Lines 5-7 will be modified differently on each branch
print("Version: 1.5")
print("Status: Production")
print("Mode: Basic")
# End of conflicting section
print("Application ready!")
if __name__ == "__main__":
main()
"@
Set-Content -Path "app.py" -Value $mainContent
git add .
git commit -m "Update to version 1.5 for production" | 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 "`nThe conflict scenario:" -ForegroundColor Cyan
Write-Host " - Main branch: Version 1.5, Production, Basic mode" -ForegroundColor White
Write-Host " - Feature branch: Version 2.0, Beta, Advanced mode" -ForegroundColor White
Write-Host " - SAME LINES modified differently = CONFLICT!" -ForegroundColor Yellow
Write-Host "`nNext steps:" -ForegroundColor Cyan
Write-Host " 1. cd challenge" -ForegroundColor White
Write-Host " 2. Try to merge: git merge feature-updates" -ForegroundColor White
Write-Host " 3. See the conflict! git status will show 'both modified: app.py'" -ForegroundColor White
Write-Host " 4. Open app.py and look for conflict markers (<<<<<<< ======= >>>>>>>)" -ForegroundColor White
Write-Host " 5. Manually resolve by editing app.py" -ForegroundColor White
Write-Host " 6. Remove conflict markers and keep desired version" -ForegroundColor White
Write-Host " 7. Stage: git add app.py" -ForegroundColor White
Write-Host " 8. Complete: git commit" -ForegroundColor White
Write-Host " 9. Run '..\verify.ps1' to check your solution" -ForegroundColor White
Write-Host ""

View File

@@ -1,123 +0,0 @@
#!/usr/bin/env pwsh
<#
.SYNOPSIS
Verifies the Module 05 challenge solution.
.DESCRIPTION
This script checks that:
- The challenge directory exists
- A Git repository exists
- No merge conflicts remain
- The merge was completed successfully
- Conflict markers have been removed from files
#>
Write-Host "`n=== Verifying Module 05 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 there are any unresolved conflicts
$gitStatus = git status --porcelain 2>$null
$hasConflicts = git status | Select-String "Unmerged paths" 2>$null
if ($hasConflicts) {
Write-Host "[FAIL] Unresolved merge conflicts still exist" -ForegroundColor Red
Write-Host "[HINT] Open app.py, resolve conflicts, then: git add app.py && git commit" -ForegroundColor Yellow
$allChecksPassed = $false
} else {
Write-Host "[PASS] No unresolved conflicts" -ForegroundColor Green
}
# Check if app.py exists
if (-not (Test-Path "app.py")) {
Write-Host "[FAIL] app.py not found" -ForegroundColor Red
Write-Host "[HINT] Did you run setup.ps1?" -ForegroundColor Yellow
$allChecksPassed = $false
Set-Location ..
exit 1
}
# Check if app.py contains conflict markers
$appContent = Get-Content "app.py" -Raw
if ($appContent -match "<<<<<<< HEAD" -or $appContent -match "=======" -or $appContent -match ">>>>>>>") {
Write-Host "[FAIL] Conflict markers still present in app.py" -ForegroundColor Red
Write-Host "[HINT] Edit app.py to remove all conflict markers (<<<<<<< ======= >>>>>>>)" -ForegroundColor Yellow
$allChecksPassed = $false
} else {
Write-Host "[PASS] No conflict markers in app.py" -ForegroundColor Green
}
# Check if a merge commit exists
$mergeCommits = git log --merges --oneline 2>$null
if ($mergeCommits) {
Write-Host "[PASS] Merge commit exists (conflict was resolved and committed)" -ForegroundColor Green
} else {
# Check if we're in the middle of a merge
if (Test-Path ".git/MERGE_HEAD") {
Write-Host "[FAIL] Merge not completed - still in progress" -ForegroundColor Red
Write-Host "[HINT] After resolving conflicts and staging with 'git add', run 'git commit'" -ForegroundColor Yellow
$allChecksPassed = $false
} else {
Write-Host "[FAIL] No merge commit found" -ForegroundColor Red
Write-Host "[HINT] Did you merge feature-updates into main?" -ForegroundColor Yellow
$allChecksPassed = $false
}
}
# Check that file has actual content (not just conflict markers removed)
if ($appContent -match "def main\(\):" -and $appContent -match "print") {
Write-Host "[PASS] app.py contains valid Python code" -ForegroundColor Green
} else {
Write-Host "[FAIL] app.py appears to be incomplete or damaged" -ForegroundColor Red
$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 resolved a merge conflict!" -ForegroundColor Cyan
Write-Host "You now understand:" -ForegroundColor Cyan
Write-Host " - How merge conflicts occur (same lines changed differently)" -ForegroundColor White
Write-Host " - How to recognize conflict markers" -ForegroundColor White
Write-Host " - How to manually resolve conflicts" -ForegroundColor White
Write-Host " - How to complete a merge after resolution (git add + git commit)" -ForegroundColor White
Write-Host "`nThis is a crucial skill for collaborative development!" -ForegroundColor Green
Write-Host "Ready 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 "[TIP] If stuck, run 'git status' to see what state you're in." -ForegroundColor Cyan
Write-Host ""
exit 1
}

199
module-10-stash/README.md Normal file
View 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
View 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
View 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
View 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

252
module-12-remotes/README.md Normal file
View File

@@ -0,0 +1,252 @@
# Module 12: Working with Remotes
## Learning Objectives
By the end of this module, you will:
- Understand what remote repositories are
- Clone a repository from a remote source
- Push local commits to a remote repository
- Pull changes from a remote repository
- Understand the difference between fetch and pull
- Manage remote branches
- Work with remote tracking branches
## Challenge Description
You're joining a team project that's already hosted on a remote server. You need to clone the repository, make changes, and synchronize your work with the remote.
Your task is to:
1. Clone the remote repository
2. Create a new branch for your feature
3. Make changes and commit them locally
4. Push your branch to the remote
5. Fetch updates that were made by teammates
6. Merge remote changes into your branch
## Key Concepts
### What is a Remote Repository?
A remote repository is a version of your project hosted on a server (like GitHub, GitLab, or Bitbucket) or another location. It allows teams to collaborate by sharing code.
### Common Remote Operations
**Clone**: Create a local copy of a remote repository
```
Remote Server Your Computer
[repo] -----------------> [local copy of repo]
```
**Push**: Send your local commits to the remote
```
Your Computer Remote Server
[commits] -----------------> [repo updated]
```
**Pull**: Get changes from remote and merge into your branch
```
Remote Server Your Computer
[commits] ---------------> [branch updated]
```
**Fetch**: Get changes from remote but don't merge yet
```
Remote Server Your Computer
[commits] ---------------> [stored locally, not merged]
```
### Origin vs Upstream
- **origin**: The default name for the remote you cloned from
- **upstream**: Often used for the original repository when you've forked it
- You can have multiple remotes with different names
### Remote Tracking Branches
When you clone a repository, Git creates remote tracking branches:
- `origin/main` - tracks the main branch on origin
- `origin/feature` - tracks the feature branch on origin
These are read-only local copies that show the state of remote branches.
## Useful Commands
```bash
# Clone a repository
git clone <url>
git clone <url> <directory-name>
# View remotes
git remote
git remote -v # Show URLs
# Add a remote
git remote add <name> <url>
# Remove a remote
git remote remove <name>
# Rename a remote
git remote rename <old-name> <new-name>
# Push to remote
git push origin <branch-name>
git push -u origin <branch-name> # Set upstream tracking
# Pull from remote (fetch + merge)
git pull
git pull origin <branch-name>
# Fetch from remote (no merge)
git fetch
git fetch origin
# See remote branches
git branch -r
git branch -a # All branches (local and remote)
# Delete remote branch
git push origin --delete <branch-name>
# Update remote tracking information
git remote update
git remote prune origin # Remove stale remote tracking branches
```
## Verification
Run the verification script to check your solution:
```bash
.\verify.ps1
```
The verification will check that:
- You cloned the repository correctly
- Your feature branch exists and has commits
- Changes were pushed to the remote
- You fetched and merged remote updates
- Your branch is up to date
## Challenge Steps
1. Navigate to the challenge directory
2. You'll find a simulated "remote" repository
3. Clone it: `git clone remote-repo local-repo`
4. Navigate into your clone: `cd local-repo`
5. Create and switch to a feature branch: `git checkout -b add-feature`
6. Make changes to the project (add a new feature to app.js)
7. Commit your changes
8. Push to remote: `git push -u origin add-feature`
9. Simulate teammate changes (run the provided update script)
10. Fetch updates: `git fetch origin`
11. View remote changes: `git log origin/main`
12. Merge remote main into your branch: `git merge origin/main`
13. Run verification
## Tips
- `git clone` automatically sets up the remote as "origin"
- `git push -u` sets up tracking so future pushes can just use `git push`
- Use `git fetch` to see what's changed before merging
- `git pull` = `git fetch` + `git merge`
- Always pull before pushing to avoid conflicts
- Use `git branch -a` to see all local and remote branches
- Remote branches are read-only; you work on local branches
- `origin/main` is a remote tracking branch, `main` is your local branch
## Push vs Pull vs Fetch
### Git Push
Uploads your local commits to the remote:
```bash
git push origin main
```
Use when: You have local commits ready to share with the team
### Git Pull
Downloads and merges remote changes:
```bash
git pull origin main
```
Use when: You want to update your branch with remote changes
Equivalent to: `git fetch origin` + `git merge origin/main`
### Git Fetch
Downloads remote changes without merging:
```bash
git fetch origin
```
Use when: You want to see what's changed before merging
Safer than pull because it doesn't automatically merge
## Common Remote Workflows
### Daily Work Flow
```bash
# Start of day: get latest changes
git checkout main
git pull origin main
# Create feature branch
git checkout -b my-feature
# Do work, make commits
git add .
git commit -m "Add feature"
# Before pushing, update with latest main
git checkout main
git pull origin main
git checkout my-feature
git merge main
# Push your feature
git push -u origin my-feature
```
### Collaboration Workflow
```bash
# Teammate pushed changes to main
git fetch origin
git log origin/main # Review changes
git merge origin/main # Merge into current branch
# Or use pull (fetch + merge in one step)
git pull origin main
```
### Syncing Fork (with upstream)
```bash
# Add original repo as upstream
git remote add upstream <original-repo-url>
# Get latest from upstream
git fetch upstream
git checkout main
git merge upstream/main
# Push to your fork
git push origin main
```
## Handling Push Rejection
If push is rejected because remote has changes:
```bash
# Remote has commits you don't have
git push origin main
# Error: Updates were rejected
# Solution 1: Pull first (creates merge commit)
git pull origin main
git push origin main
# Solution 2: Pull with rebase (cleaner history)
git pull --rebase origin main
git push origin main
```
## What You'll Learn
Working with remotes is fundamental to team collaboration. Understanding the difference between local and remote branches, knowing when to push/pull/fetch, and managing synchronization are core skills for any developer. While this module uses a local "remote" for learning, the concepts apply directly to GitHub, GitLab, and other hosting services. Mastering remotes enables you to work effectively in distributed teams and contribute to open source projects.

View File

@@ -0,0 +1,22 @@
#!/usr/bin/env pwsh
<#
.SYNOPSIS
Resets the remotes 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"

151
module-12-remotes/setup.ps1 Normal file
View File

@@ -0,0 +1,151 @@
#!/usr/bin/env pwsh
<#
.SYNOPSIS
Sets up the remotes challenge environment.
.DESCRIPTION
Creates a simulated remote repository and working environment
for learning Git remote operations.
#>
# 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 structure
Write-Host "Creating challenge environment..." -ForegroundColor Cyan
New-Item -ItemType Directory -Path "challenge" | Out-Null
Set-Location "challenge"
# Create a temporary workspace to build the initial repository
New-Item -ItemType Directory -Path "temp-workspace" | Out-Null
Set-Location "temp-workspace"
# Initialize and create initial commits
git init | Out-Null
git config user.name "Workshop User" | Out-Null
git config user.email "user@workshop.local" | Out-Null
# Create initial project files
$app = @"
class Application {
constructor() {
this.name = 'TeamProject';
this.version = '1.0.0';
}
start() {
console.log('Application started');
}
}
module.exports = Application;
"@
Set-Content -Path "app.js" -Value $app
git add app.js
git commit -m "Initial application" | Out-Null
$readme = @"
# Team Project
A collaborative project for learning Git remotes.
## Features
- Basic application structure
"@
Set-Content -Path "README.md" -Value $readme
git add README.md
git commit -m "Add README" | Out-Null
$package = @"
{
"name": "team-project",
"version": "1.0.0",
"description": "Learning Git remotes",
"main": "app.js"
}
"@
Set-Content -Path "package.json" -Value $package
git add package.json
git commit -m "Add package.json" | Out-Null
# Create the "remote" repository (bare repository)
Set-Location ..
git clone --bare temp-workspace remote-repo 2>$null | Out-Null
# Clean up temp workspace
Remove-Item -Path "temp-workspace" -Recurse -Force
# Create a helper script to simulate teammate changes
$simulateTeammateScript = @"
#!/usr/bin/env pwsh
# This script simulates a teammate making changes to the remote repository
Write-Host "Simulating teammate changes..." -ForegroundColor Cyan
# Create a temporary clone
if (Test-Path "temp-teammate") {
Remove-Item -Path "temp-teammate" -Recurse -Force
}
git clone remote-repo temp-teammate 2>>`$null | Out-Null
Set-Location temp-teammate
git config user.name "Teammate" 2>>`$null | Out-Null
git config user.email "teammate@workshop.local" 2>>`$null | Out-Null
# Make changes to main branch
`$appContent = Get-Content "app.js" -Raw
`$updatedApp = `$appContent -replace "start\(\) {", @"
start() {
console.log('Starting application...');
this.initialize();
}
initialize() {
"@
Set-Content -Path "app.js" -Value `$updatedApp
git add app.js 2>>`$null
git commit -m "Add initialization method" 2>>`$null | Out-Null
git push origin main 2>>`$null | Out-Null
Set-Location ..
Remove-Item -Path "temp-teammate" -Recurse -Force
Write-Host "Teammate pushed changes to remote repository!" -ForegroundColor Green
Write-Host "Use 'git fetch origin' to see the changes" -ForegroundColor Cyan
"@
Set-Content -Path "simulate-teammate.ps1" -Value $simulateTeammateScript
# Return to module directory
Set-Location ..
Write-Host "`n========================================" -ForegroundColor Green
Write-Host "Challenge environment created!" -ForegroundColor Green
Write-Host "========================================" -ForegroundColor Green
Write-Host "`nSetup complete! You have:" -ForegroundColor Cyan
Write-Host "- remote-repo/ - A 'remote' repository (simulates GitHub/GitLab)" -ForegroundColor White
Write-Host "- simulate-teammate.ps1 - Script to simulate teammate changes" -ForegroundColor White
Write-Host "`nYour task:" -ForegroundColor Yellow
Write-Host "1. Navigate to the challenge directory: cd challenge" -ForegroundColor White
Write-Host "2. Clone the remote: git clone remote-repo local-repo" -ForegroundColor White
Write-Host "3. Navigate to your clone: cd local-repo" -ForegroundColor White
Write-Host "4. Create a feature branch: git checkout -b add-feature" -ForegroundColor White
Write-Host "5. Add a new method to app.js (e.g., stop method)" -ForegroundColor White
Write-Host "6. Commit your changes" -ForegroundColor White
Write-Host "7. Push your branch: git push -u origin add-feature" -ForegroundColor White
Write-Host "8. Go back to challenge directory: cd .." -ForegroundColor White
Write-Host "9. Simulate teammate changes: pwsh simulate-teammate.ps1" -ForegroundColor White
Write-Host "10. Go back to local-repo: cd local-repo" -ForegroundColor White
Write-Host "11. Fetch updates: git fetch origin" -ForegroundColor White
Write-Host "12. Merge remote main: git merge origin/main" -ForegroundColor White
Write-Host "`nRun '../verify.ps1' from the challenge directory to check your solution.`n" -ForegroundColor Cyan

View File

@@ -0,0 +1,174 @@
#!/usr/bin/env pwsh
<#
.SYNOPSIS
Verifies the remotes challenge solution.
.DESCRIPTION
Checks that the user successfully cloned the repository, worked with
remotes, pushed branches, and synchronized changes.
#>
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 local-repo exists
if (-not (Test-Path "local-repo")) {
Write-Host "[FAIL] local-repo directory not found." -ForegroundColor Red
Write-Host "Hint: Clone the remote repository with: git clone remote-repo local-repo" -ForegroundColor Yellow
Set-Location ..
exit 1
}
Set-Location "local-repo"
# Check if it's a git repository
if (-not (Test-Path ".git")) {
Write-Host "[FAIL] local-repo is not a git repository." -ForegroundColor Red
Set-Location ../..
exit 1
}
# Check if remote 'origin' is configured
$remotes = git remote 2>$null
if ($remotes -notcontains "origin") {
Write-Host "[FAIL] No remote named 'origin' found." -ForegroundColor Red
Write-Host "Hint: Cloning should automatically set up 'origin' as the remote" -ForegroundColor Yellow
Set-Location ../..
exit 1
}
# Check if origin points to the remote-repo
$originUrl = git remote get-url origin 2>$null
if ($originUrl -notmatch "remote-repo") {
Write-Host "[FAIL] Origin remote does not point to remote-repo." -ForegroundColor Red
Write-Host "Origin URL: $originUrl" -ForegroundColor Yellow
Set-Location ../..
exit 1
}
Write-Host "[PASS] Repository cloned correctly with origin remote!" -ForegroundColor Green
# Check if add-feature branch exists locally
$branches = git branch 2>$null
if ($branches -notmatch "add-feature") {
Write-Host "[FAIL] add-feature branch not found locally." -ForegroundColor Red
Write-Host "Hint: Create the branch with: git checkout -b add-feature" -ForegroundColor Yellow
Set-Location ../..
exit 1
}
# Switch to add-feature branch
git checkout add-feature 2>$null | Out-Null
# Check if there are commits on add-feature beyond the initial commits
$featureCommitCount = (git rev-list --count add-feature 2>$null)
$mainCommitCount = (git rev-list --count main 2>$null)
if ($featureCommitCount -le $mainCommitCount) {
Write-Host "[FAIL] add-feature branch has no new commits." -ForegroundColor Red
Write-Host "Hint: Make changes to app.js and commit them on the add-feature branch" -ForegroundColor Yellow
Set-Location ../..
exit 1
}
Write-Host "[PASS] Feature branch created with commits!" -ForegroundColor Green
# Check if add-feature branch was pushed to remote
Set-Location ..
Set-Location remote-repo
$remoteBranches = git branch 2>$null
if ($remoteBranches -notmatch "add-feature") {
Write-Host "[FAIL] add-feature branch not found on remote." -ForegroundColor Red
Write-Host "Hint: Push your branch with: git push -u origin add-feature" -ForegroundColor Yellow
Set-Location ../..
exit 1
}
Write-Host "[PASS] Feature branch pushed to remote!" -ForegroundColor Green
Set-Location ../local-repo
# Check if app.js has been modified
if (-not (Test-Path "app.js")) {
Write-Host "[FAIL] app.js not found." -ForegroundColor Red
Set-Location ../..
exit 1
}
$appContent = Get-Content "app.js" -Raw
# Check for user's changes (should have added something)
$featureCommits = git log --pretty=format:"%s" add-feature 2>$null
if (-not ($featureCommits -match "stop|feature|add" -or $appContent -match "stop")) {
Write-Host "[FAIL] No new feature detected in app.js." -ForegroundColor Red
Write-Host "Hint: Add a new method (like 'stop') to app.js and commit it" -ForegroundColor Yellow
Set-Location ../..
exit 1
}
# Check if teammate changes were fetched and merged
# The teammate's change adds an 'initialize' method
if ($appContent -notmatch "initialize") {
Write-Host "[FAIL] Teammate's changes not merged into your branch." -ForegroundColor Red
Write-Host "Hint: Did you run the simulate-teammate.ps1 script?" -ForegroundColor Yellow
Write-Host " Then: git fetch origin" -ForegroundColor Yellow
Write-Host " git merge origin/main" -ForegroundColor Yellow
Set-Location ../..
exit 1
}
Write-Host "[PASS] Remote changes fetched and merged!" -ForegroundColor Green
# Check that the branch has both sets of changes
$allCommits = git log --all --pretty=format:"%s" 2>$null
$hasUserCommit = $allCommits -match "stop|feature|add"
$hasTeammateCommit = $allCommits -match "initialization|initialize"
if (-not $hasTeammateCommit) {
Write-Host "[WARNING] Teammate commit not found in history." -ForegroundColor Yellow
Write-Host "Make sure you ran simulate-teammate.ps1 and fetched the changes" -ForegroundColor Yellow
}
# Check for remote tracking
$tracking = git branch -vv 2>$null
if ($tracking -notmatch "origin/add-feature") {
Write-Host "[WARNING] add-feature branch may not be tracking origin/add-feature" -ForegroundColor Yellow
Write-Host "Hint: Use 'git push -u origin add-feature' to set up tracking" -ForegroundColor Yellow
}
# 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 "- Cloned the remote repository" -ForegroundColor White
Write-Host "- Created a feature branch" -ForegroundColor White
Write-Host "- Made commits to your branch" -ForegroundColor White
Write-Host "- Pushed your branch to the remote" -ForegroundColor White
Write-Host "- Fetched changes from the remote" -ForegroundColor White
Write-Host "- Merged remote changes into your branch" -ForegroundColor White
Write-Host "`nYou now understand how to work with Git remotes!" -ForegroundColor Green
Write-Host "`nKey takeaways:" -ForegroundColor Yellow
Write-Host "- Clone creates a local copy linked to the remote" -ForegroundColor White
Write-Host "- Push uploads your commits to the remote" -ForegroundColor White
Write-Host "- Fetch downloads remote changes without merging" -ForegroundColor White
Write-Host "- Pull = Fetch + Merge" -ForegroundColor White
Write-Host "`nThese skills are essential for team collaboration!`n" -ForegroundColor Green
Set-Location ../..
exit 0