feat: add remotes and stash
This commit is contained in:
@@ -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.
|
||||
@@ -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"
|
||||
@@ -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 ""
|
||||
@@ -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
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
|
||||
252
module-12-remotes/README.md
Normal file
252
module-12-remotes/README.md
Normal 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.
|
||||
22
module-12-remotes/reset.ps1
Normal file
22
module-12-remotes/reset.ps1
Normal 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
151
module-12-remotes/setup.ps1
Normal 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
|
||||
174
module-12-remotes/verify.ps1
Normal file
174
module-12-remotes/verify.ps1
Normal 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
|
||||
Reference in New Issue
Block a user