Compare commits

...

8 Commits

Author SHA1 Message Date
Bjarke Sporring
0a9f7c1842 feat: update README after big changes 2026-01-07 23:51:49 +01:00
Bjarke Sporring
7f34cf2d08 feat: add multiplayer guidelines 2026-01-07 23:46:32 +01:00
Bjarke Sporring
eb63970b7a feat: make merge-conflicts more explicit 2026-01-07 23:03:18 +01:00
Bjarke Sporring
dff82c847c feat: add more commits to the merging challenges 2026-01-07 23:02:55 +01:00
Bjarke Sporring
c057fd617b fix: use switch and not checkout 2026-01-07 22:04:44 +01:00
Bjarke Sporring
f48eefee10 refactor: simplify 01_essentials/04-merging 2026-01-07 21:59:21 +01:00
Bjarke Sporring
df9a2bf7c1 refactor: split out merge strategies from essentials 2026-01-07 21:50:59 +01:00
Bjarke Sporring
7fb84560f5 use switch instead of checkout for branches 2026-01-07 21:37:21 +01:00
40 changed files with 4298 additions and 894 deletions

View File

@@ -43,12 +43,12 @@ Your goal is to create a feature branch, make commits on it, and understand how
5. Stage and commit: `git add login.py` and `git commit -m "Add login functionality"` 5. Stage and commit: `git add login.py` and `git commit -m "Add login functionality"`
6. Modify `login.py`, then commit again 6. Modify `login.py`, then commit again
7. Switch back to main: `git switch main` 7. Switch back to main: `git switch main`
8. Run `ls` and notice that `login.py` doesn't exist on main! 8. Run `ls` (or check your file explorer) and notice that `login.py` doesn't exist on main!
9. Switch back to feature-login: `git switch feature-login` 9. Switch back to feature-login: `git switch feature-login`
10. Run `ls` again and see that `login.py` is back! 10. Run `ls` (or check your file explorer) again and see that `login.py` is back!
> **Important Notes:** > **Important Notes:**
> - Use `git switch` to change branches (modern Git command) > - Use `git switch` to change branches
> - `git switch -c <name>` creates and switches in one command > - `git switch -c <name>` creates and switches in one command
> - Branches are independent - files in one branch don't affect another until you merge > - Branches are independent - files in one branch don't affect another until you merge
> - You can switch between branches as many times as you want > - You can switch between branches as many times as you want

View File

@@ -76,10 +76,10 @@ Write-Host "`n=== Setup Complete! ===" -ForegroundColor Green
Write-Host "`nYour challenge environment is ready in the 'challenge/' directory." -ForegroundColor Cyan Write-Host "`nYour challenge environment is ready in the 'challenge/' directory." -ForegroundColor Cyan
Write-Host "`nNext steps:" -ForegroundColor Cyan Write-Host "`nNext steps:" -ForegroundColor Cyan
Write-Host " 1. cd challenge" -ForegroundColor White Write-Host " 1. cd challenge" -ForegroundColor White
Write-Host " 2. Create a new branch: git checkout -b feature-login" -ForegroundColor White Write-Host " 2. Create a new branch: git switch -c feature-login" -ForegroundColor White
Write-Host " 3. Create login.py and commit it" -ForegroundColor White Write-Host " 3. Create login.py and commit it" -ForegroundColor White
Write-Host " 4. Make another commit on the feature branch" -ForegroundColor White Write-Host " 4. Make another commit on the feature branch" -ForegroundColor White
Write-Host " 5. Switch back to main: git checkout main" -ForegroundColor White Write-Host " 5. Switch back to main: git switch main" -ForegroundColor White
Write-Host " 6. Observe that login.py doesn't exist on main!" -ForegroundColor White Write-Host " 6. Observe that login.py doesn't exist on main!" -ForegroundColor White
Write-Host " 7. Run '..\verify.ps1' to check your solution" -ForegroundColor White Write-Host " 7. Run '..\verify.ps1' to check your solution" -ForegroundColor White
Write-Host "" Write-Host ""

View File

@@ -40,7 +40,7 @@ if ($branchExists) {
Write-Host "[PASS] Branch 'feature-login' exists" -ForegroundColor Green Write-Host "[PASS] Branch 'feature-login' exists" -ForegroundColor Green
} else { } else {
Write-Host "[FAIL] Branch 'feature-login' not found" -ForegroundColor Red Write-Host "[FAIL] Branch 'feature-login' not found" -ForegroundColor Red
Write-Host "[HINT] Create the branch with: git checkout -b feature-login" -ForegroundColor Yellow Write-Host "[HINT] Create the branch with: git switch -c feature-login" -ForegroundColor Yellow
$allChecksPassed = $false $allChecksPassed = $false
Set-Location .. Set-Location ..
exit 1 exit 1
@@ -67,7 +67,7 @@ if (Test-Path "login.py") {
} }
# Switch to main and verify login.py doesn't exist # Switch to main and verify login.py doesn't exist
git checkout main 2>$null | Out-Null git switch main 2>$null | Out-Null
if (-not (Test-Path "login.py")) { if (-not (Test-Path "login.py")) {
Write-Host "[PASS] File 'login.py' does NOT exist in main branch (branches are independent!)" -ForegroundColor Green Write-Host "[PASS] File 'login.py' does NOT exist in main branch (branches are independent!)" -ForegroundColor Green
} else { } else {
@@ -78,7 +78,7 @@ if (-not (Test-Path "login.py")) {
# Switch back to original branch # Switch back to original branch
if ($originalBranch) { if ($originalBranch) {
git checkout $originalBranch 2>$null | Out-Null git switch $originalBranch 2>$null | Out-Null
} }
Set-Location .. Set-Location ..
@@ -91,7 +91,7 @@ if ($allChecksPassed) {
Write-Host "=====================================" -ForegroundColor Green Write-Host "=====================================" -ForegroundColor Green
Write-Host "`nYou've successfully learned about Git branches!" -ForegroundColor Cyan Write-Host "`nYou've successfully learned about Git branches!" -ForegroundColor Cyan
Write-Host "You now understand:" -ForegroundColor Cyan Write-Host "You now understand:" -ForegroundColor Cyan
Write-Host " - How to create branches with git checkout -b" -ForegroundColor White Write-Host " - How to create branches with git switch -c" -ForegroundColor White
Write-Host " - How to switch between branches" -ForegroundColor White Write-Host " - How to switch between branches" -ForegroundColor White
Write-Host " - That branches are independent lines of development" -ForegroundColor White Write-Host " - That branches are independent lines of development" -ForegroundColor White
Write-Host " - That files in one branch don't affect another" -ForegroundColor White Write-Host " - That files in one branch don't affect another" -ForegroundColor White

View File

@@ -1,13 +1,12 @@
# Module 04: Merging # Module 04: Merging Branches
## Learning Objectives ## Learning Objectives
In this module, you will: In this module, you will:
- Understand what merging means in Git - Understand what merging means in Git
- Perform a fast-forward merge - Merge a feature branch back into main
- Perform a three-way merge
- Understand when merge commits are created
- Use `git merge` to combine branches - Use `git merge` to combine branches
- Visualize merged branches with `git log --graph`
## Challenge ## Challenge
@@ -19,72 +18,191 @@ Run the setup script to create your challenge environment:
.\setup.ps1 .\setup.ps1
``` ```
This will create a `challenge/` directory with a Git repository that has a main branch and a feature branch ready to merge. This will create a `challenge/` directory with a Git repository that has a main branch and a feature branch.
### Your Task ### Your Task
This challenge has two parts that teach you about the two types of merges in Git: You've been working on a new login feature in a separate branch. Now it's time to merge your work back into the main branch!
**Part 1: Fast-Forward Merge** **Scenario:**
1. Merge the existing `feature-api` branch into main - You created a `feature-login` branch to add login functionality
2. Observe that this is a "fast-forward" merge (no merge commit created) - You made some commits on that branch
- Meanwhile, your teammate updated the README on main
- Now you need to bring your login feature back into main
**Part 2: Three-Way Merge** **Steps:**
3. Create a new branch called `feature-ui`
4. Make commits on the feature-ui branch
5. Switch back to main and make a commit there too (creates divergence)
6. Merge feature-ui into main
7. Observe that this creates a merge commit (three-way merge)
**Suggested Approach:**
1. Navigate to the challenge directory: `cd challenge` 1. Navigate to the challenge directory: `cd challenge`
2. Check current branch: `git branch` (should be on main) 2. Check which branch you're on: `git branch`
3. View existing branches: `git branch -a` 3. View all branches: `git log --oneline --graph --all`
4. Merge feature-api: `git merge feature-api` 4. Merge the feature-login branch into main: `git merge feature-login`
5. View the log: `git log --oneline --graph` 5. View the result: `git log --oneline --graph --all`
6. Create feature-ui branch: `git switch -c feature-ui`
7. Create a new file `ui.py` and commit it
8. Make another commit on feature-ui (modify ui.py)
9. Switch back to main: `git switch main`
10. Make a change on main (modify api.py) and commit it
11. Merge feature-ui: `git merge feature-ui`
12. View the merge history: `git log --oneline --graph --all`
> **Important Notes:** > **That's it!** Merging is how you bring work from one branch into another.
> - A **fast-forward merge** happens when main hasn't changed since the feature branch was created
> - A **three-way merge** creates a merge commit when both branches have diverged
> - You can see merge commits with `git log --merges`
> - The `--graph` option helps visualize the branch history
> - After merging, the feature branch still exists but you can delete it with `git branch -d`
## Key Concepts ## What is Merging?
- **Merge**: Combining changes from different branches into one branch. **Merging** is the process of taking changes from one branch and bringing them into another branch.
- **Fast-Forward Merge**: When the target branch hasn't changed, Git simply moves the branch pointer forward. No merge commit is created.
- **Three-Way Merge**: When both branches have new commits, Git creates a merge commit that has two parent commits. Think of it like combining two streams into one river - all the water (code) flows together.
- **Merge Commit**: A special commit with two (or more) parent commits, representing the point where branches were merged.
- **Divergent Branches**: Branches that have different commits since they split from a common ancestor. ### Before Merging
You have two branches with different work:
```
main: A---B---C
\
feature-login: D---E
```
- Main branch has commits A, B, and C
- Feature-login branch has commits D and E
- They split apart at commit B
### After Merging
You bring the feature branch into main:
```
main: A---B---C---M
\ /
feature-login: D-E
```
- Commit M is a **merge commit** - it combines both branches
- Main now has all the work from both branches
- Your login feature is now part of main!
## How to Merge
Merging is simple - just two steps:
**1. Switch to the branch you want to merge INTO**
```bash
git switch main
```
This is the branch that will receive the changes.
**2. Merge the other branch**
```bash
git merge feature-login
```
This brings changes from feature-login into main.
**That's it!** Git does the work of combining the changes.
## Understanding the Merge Commit
When you merge, Git creates a special commit called a **merge commit**.
**What makes it special?**
- It has TWO parent commits (one from each branch)
- It represents the point where branches came back together
- Git writes a message like "Merge branch 'feature-login'"
You can view the merge commit just like any other commit:
```bash
git show HEAD
```
## Visualizing Merges
Use `git log --graph` to see how branches merged:
```bash
git log --oneline --graph --all
```
**Example output:**
```
* a1b2c3d (HEAD -> main) Merge branch 'feature-login'
|\
| * e5f6g7h (feature-login) Implement login validation
| * h8i9j0k Add login form
* | k1l2m3n Update README with setup instructions
|/
* n4o5p6q Initial project structure
```
**Reading the graph:**
- `*` = A commit
- `|` = Branch line
- `/` and `\` = Branches splitting or joining
- `(HEAD -> main)` = You are here
The graph shows how the branches split and came back together!
## Useful Commands ## Useful Commands
```bash ```bash
git merge <branch> # Merge branch into current branch # Merging
git log --oneline --graph # View merge history visually git merge <branch-name> # Merge a branch into current branch
git log --graph --all # View all branches and merges
git log --merges # Show only merge commits # Viewing
git branch -d <branch> # Delete a merged branch (optional) git log --oneline --graph # See branch history visually
git log --oneline --graph --all # Include all branches
# Branch management
git branch # List branches
git switch <branch-name> # Switch to a branch
git branch -d <branch-name> # Delete a branch (after merging)
```
## Common Questions
### "What if I'm on the wrong branch when I merge?"
Don't worry! The branch you're currently on is the one that receives the changes.
**Example:**
```bash
git switch main # Go to main
git merge feature-login # Bring feature-login INTO main
```
Always switch to the destination branch first!
### "Can I undo a merge?"
Yes! Before you push to a remote:
```bash
git reset --hard HEAD~1
```
This removes the merge commit. (We'll cover this more in later modules)
### "What happens to the feature branch after merging?"
The feature branch still exists! The merge just copies its commits into main.
You can delete it if you're done:
```bash
git branch -d feature-login # Safe delete (only if merged)
```
The commits are still in history - you're just removing the branch label.
### "What if Git can't merge automatically?"
Sometimes Git needs your help when the same lines were changed in both branches. This is called a **merge conflict**.
Don't worry - we'll learn how to handle conflicts in the next module!
If you encounter a conflict now and want to cancel:
```bash
git merge --abort
``` ```
## Verification ## Verification
Once you've completed both merges, verify your solution: Once you've merged the feature-login branch, verify your solution:
```powershell ```powershell
.\verify.ps1 .\verify.ps1
``` ```
The verification script will check that you've successfully merged both feature branches and understand the different merge types. The verification script will check that you've successfully merged.
## Need to Start Over? ## Need to Start Over?
@@ -94,4 +212,18 @@ If you want to reset the challenge and start fresh:
.\reset.ps1 .\reset.ps1
``` ```
This will remove the challenge directory and run the setup script again, giving you a clean slate. ## What's Next?
**Next module:** Merge Conflicts - Learn what to do when Git can't automatically merge changes.
**Later:** In the advanced modules, you'll learn about different merging strategies and when to use them. For now, understanding basic merging is all you need!
## Quick Summary
**Merging** combines work from one branch into another
✅ Switch to the destination branch, then run `git merge <source-branch>`
✅ Git creates a **merge commit** to record the merge
✅ Use `git log --graph` to visualize how branches merged
✅ The feature branch still exists after merging - you can delete it if you want
That's all there is to basic merging! 🎉

View File

@@ -5,8 +5,7 @@
.DESCRIPTION .DESCRIPTION
This script creates a challenge directory with a Git repository that This script creates a challenge directory with a Git repository that
contains a main branch and a feature branch ready to merge. Students contains two divergent branches ready to merge (three-way merge scenario).
will practice both fast-forward and three-way merges.
#> #>
Write-Host "`n=== Setting up Module 04 Challenge ===" -ForegroundColor Cyan Write-Host "`n=== Setting up Module 04 Challenge ===" -ForegroundColor Cyan
@@ -30,99 +29,218 @@ git init | Out-Null
git config user.name "Workshop Student" git config user.name "Workshop Student"
git config user.email "student@example.com" git config user.email "student@example.com"
# Commit 1: Initial API file on main # Commit 1: Initial project structure on main
Write-Host "Creating initial API structure on main..." -ForegroundColor Green Write-Host "Creating initial project structure..." -ForegroundColor Green
$apiContent = @" $readmeContent = @"
# api.py - API module # My Project
def api_handler(): A simple web application project.
print("API Handler initialized")
return True ## Setup
Coming soon...
"@ "@
Set-Content -Path "api.py" -Value $apiContent Set-Content -Path "README.md" -Value $readmeContent
$appContent = @"
# app.py - Main application file
def main():
print("Welcome to My App!")
pass
if __name__ == "__main__":
main()
"@
Set-Content -Path "app.py" -Value $appContent
git add . git add .
git commit -m "Initial API setup" | Out-Null git commit -m "Initial project structure" | Out-Null
# Commit 2: Add more API functionality on main # Commit 2: Add configuration file on main
$apiContent = @" Write-Host "Adding configuration..." -ForegroundColor Green
# api.py - API module $configContent = @"
# config.py - Configuration settings
def api_handler(): APP_NAME = "My Project"
print("API Handler initialized") VERSION = "1.0.0"
DEBUG = False
"@
Set-Content -Path "config.py" -Value $configContent
git add .
git commit -m "Add basic configuration" | Out-Null
# Commit 3: Add database utilities on main
Write-Host "Adding database utilities..." -ForegroundColor Green
$dbContent = @"
# database.py - Database utilities
def connect():
"""Connect to database."""
print("Connecting to database...")
return True return True
def get_data(): def disconnect():
print("Fetching data from API...") """Disconnect from database."""
return {"status": "ok"} print("Disconnecting...")
return True
"@ "@
Set-Content -Path "api.py" -Value $apiContent Set-Content -Path "database.py" -Value $dbContent
git add . git add .
git commit -m "Add get_data function" | Out-Null git commit -m "Add database utilities" | Out-Null
# Create feature-api branch and add commits # Create feature-login branch
Write-Host "Creating feature-api branch..." -ForegroundColor Green Write-Host "Creating feature-login branch..." -ForegroundColor Green
git checkout -b feature-api 2>$null | Out-Null git switch -c feature-login | Out-Null
# Commit on feature-api: Add API routes # Commit 4: Add login module on feature-login
$routesContent = @" Write-Host "Adding login functionality on feature-login..." -ForegroundColor Green
# api-routes.py - API Routes module $loginContent = @"
# login.py - User login module
def setup_routes(): def login(username, password):
print("Setting up API routes...") """Authenticate a user."""
routes = { print(f"Authenticating user: {username}")
"/api/data": "get_data", # TODO: Add actual authentication logic
"/api/status": "get_status" return True
}
return routes def logout(username):
"""Log out a user."""
print(f"Logging out user: {username}")
return True
"@ "@
Set-Content -Path "api-routes.py" -Value $routesContent Set-Content -Path "login.py" -Value $loginContent
git add . git add .
git commit -m "Add API routes" | Out-Null git commit -m "Add login module" | Out-Null
# Second commit on feature-api: Enhance routes # Commit 5: Add password validation on feature-login
$routesContent = @" $loginContent = @"
# api-routes.py - API Routes module # login.py - User login module
def setup_routes(): def validate_password(password):
print("Setting up API routes...") """Validate password strength."""
routes = { if len(password) < 8:
"/api/data": "get_data", return False
"/api/status": "get_status", return True
"/api/health": "health_check"
}
return routes
def health_check(): def login(username, password):
return {"healthy": True} """Authenticate a user."""
if not validate_password(password):
print("Password too weak!")
return False
print(f"Authenticating user: {username}")
# TODO: Add actual authentication logic
return True
def logout(username):
"""Log out a user."""
print(f"Logging out user: {username}")
return True
"@ "@
Set-Content -Path "api-routes.py" -Value $routesContent Set-Content -Path "login.py" -Value $loginContent
git add . git add .
git commit -m "Add health check route" | Out-Null git commit -m "Add password validation" | Out-Null
# Commit 6: Integrate login with app on feature-login
$appContent = @"
# app.py - Main application file
from login import login, logout
def main():
print("Welcome to My App!")
# Add login integration
if login("testuser", "password123"):
print("Login successful!")
pass
if __name__ == "__main__":
main()
"@
Set-Content -Path "app.py" -Value $appContent
git add .
git commit -m "Integrate login with main app" | Out-Null
# Commit 7: Add session management on feature-login
$sessionContent = @"
# session.py - Session management
class Session:
def __init__(self, username):
self.username = username
self.active = True
def end(self):
"""End the session."""
self.active = False
print(f"Session ended for {self.username}")
"@
Set-Content -Path "session.py" -Value $sessionContent
git add .
git commit -m "Add session management" | Out-Null
# Switch back to main branch # Switch back to main branch
Write-Host "Switching back to main branch..." -ForegroundColor Green Write-Host "Switching back to main branch..." -ForegroundColor Green
git checkout main 2>$null | Out-Null git switch main | Out-Null
# Commit 8: Update README on main (creates divergence)
Write-Host "Adding documentation on main (creates divergence)..." -ForegroundColor Green
$readmeContent = @"
# My Project
A simple web application project.
## Setup
1. Install Python 3.8 or higher
2. Run: python app.py
## Features
- User authentication (coming soon)
- Data management (coming soon)
## Contributing
Please follow our coding standards when contributing.
"@
Set-Content -Path "README.md" -Value $readmeContent
git add .
git commit -m "Update README with setup instructions" | Out-Null
# Commit 9: Update configuration on main
$configContent = @"
# config.py - Configuration settings
APP_NAME = "My Project"
VERSION = "1.0.0"
DEBUG = False
DATABASE_PATH = "./data/app.db"
LOG_LEVEL = "INFO"
"@
Set-Content -Path "config.py" -Value $configContent
git add .
git commit -m "Add database path to config" | Out-Null
# Return to module directory # Return to module directory
Set-Location .. Set-Location ..
Write-Host "`n=== Setup Complete! ===" -ForegroundColor Green Write-Host "`n=== Setup Complete! ===" -ForegroundColor Green
Write-Host "`nYour challenge environment is ready in the 'challenge/' directory." -ForegroundColor Cyan Write-Host "`nYour challenge environment is ready in the 'challenge/' directory." -ForegroundColor Cyan
Write-Host "`nYou have:" -ForegroundColor Cyan Write-Host "`nScenario: You have two divergent branches!" -ForegroundColor Yellow
Write-Host " - A main branch with API code" -ForegroundColor White Write-Host " - main: Has 5 commits (config, database utils, README updates)" -ForegroundColor White
Write-Host " - A feature-api branch with API routes ready to merge" -ForegroundColor White Write-Host " - feature-login: Has 4 commits (login, validation, session)" -ForegroundColor White
Write-Host "`nNext steps:" -ForegroundColor Cyan Write-Host "`nNext steps:" -ForegroundColor Cyan
Write-Host " 1. cd challenge" -ForegroundColor White Write-Host " 1. cd challenge" -ForegroundColor White
Write-Host " 2. View branches: git branch -a" -ForegroundColor White Write-Host " 2. View the branch structure: git log --oneline --graph --all" -ForegroundColor White
Write-Host " 3. Merge feature-api: git merge feature-api (fast-forward merge)" -ForegroundColor White Write-Host " 3. Merge feature-login into main: git merge feature-login" -ForegroundColor White
Write-Host " 4. Create feature-ui branch: git checkout -b feature-ui" -ForegroundColor White Write-Host " 4. View the merge result: git log --oneline --graph --all" -ForegroundColor White
Write-Host " 5. Make commits on feature-ui" -ForegroundColor White Write-Host " 5. Run '..\verify.ps1' to check your solution" -ForegroundColor White
Write-Host " 6. Switch back to main and make a commit there" -ForegroundColor White
Write-Host " 7. Merge feature-ui: git merge feature-ui (three-way merge)" -ForegroundColor White
Write-Host " 8. View merge history: git log --oneline --graph --all" -ForegroundColor White
Write-Host " 9. Run '..\verify.ps1' to check your solution" -ForegroundColor White
Write-Host "" Write-Host ""

View File

@@ -8,9 +8,9 @@
- The challenge directory exists - The challenge directory exists
- A Git repository exists - A Git repository exists
- Currently on main branch - Currently on main branch
- feature-api has been merged into main - feature-login has been merged into main
- feature-ui branch exists and has been merged - A merge commit exists (three-way merge)
- A merge commit exists (from three-way merge) - Login functionality is present on main
#> #>
Write-Host "`n=== Verifying Module 04 Solution ===" -ForegroundColor Cyan Write-Host "`n=== Verifying Module 04 Solution ===" -ForegroundColor Cyan
@@ -38,82 +38,63 @@ if ($currentBranch -eq "main") {
Write-Host "[PASS] Currently on main branch" -ForegroundColor Green Write-Host "[PASS] Currently on main branch" -ForegroundColor Green
} else { } else {
Write-Host "[FAIL] Not on main branch (currently on: $currentBranch)" -ForegroundColor Red Write-Host "[FAIL] Not on main branch (currently on: $currentBranch)" -ForegroundColor Red
Write-Host "[HINT] Switch to main with: git checkout main" -ForegroundColor Yellow Write-Host "[HINT] Switch to main with: git switch main" -ForegroundColor Yellow
$allChecksPassed = $false $allChecksPassed = $false
} }
# Check if feature-api branch exists # Check if feature-login branch exists
$featureApiBranch = git branch --list "feature-api" 2>$null $featureLoginBranch = git branch --list "feature-login" 2>$null
if ($featureApiBranch) { if ($featureLoginBranch) {
Write-Host "[PASS] Branch 'feature-api' exists" -ForegroundColor Green Write-Host "[PASS] Branch 'feature-login' exists" -ForegroundColor Green
} else { } else {
Write-Host "[INFO] Branch 'feature-api' not found (may have been deleted after merge)" -ForegroundColor Cyan Write-Host "[INFO] Branch 'feature-login' not found (may have been deleted after merge)" -ForegroundColor Cyan
} }
# Check if commits from feature-api are in main # Check if login.py exists (should be on main after merge)
$apiRoutesCommit = git log --all --grep="Add API routes" --oneline 2>$null if (Test-Path "login.py") {
$healthCheckCommit = git log --all --grep="Add health check route" --oneline 2>$null Write-Host "[PASS] File 'login.py' exists on main (from feature-login merge)" -ForegroundColor Green
} else {
Write-Host "[FAIL] File 'login.py' not found on main" -ForegroundColor Red
Write-Host "[HINT] This file should appear after merging feature-login into main" -ForegroundColor Yellow
$allChecksPassed = $false
}
if ($apiRoutesCommit -and $healthCheckCommit) { # Check if app.py contains login integration
# Check if these commits are in main's history if (Test-Path "app.py") {
$apiRoutesInMain = git log --grep="Add API routes" --oneline 2>$null $appContent = Get-Content "app.py" -Raw
$healthCheckInMain = git log --grep="Add health check route" --oneline 2>$null if ($appContent -match "login") {
Write-Host "[PASS] app.py contains login integration" -ForegroundColor Green
if ($apiRoutesInMain -and $healthCheckInMain) {
Write-Host "[PASS] Commits from feature-api are merged into main" -ForegroundColor Green
} else { } else {
Write-Host "[FAIL] Commits from feature-api not found in main" -ForegroundColor Red Write-Host "[FAIL] app.py doesn't contain login integration" -ForegroundColor Red
Write-Host "[HINT] Merge feature-api with: git merge feature-api" -ForegroundColor Yellow Write-Host "[HINT] After merging, app.py should import and use the login module" -ForegroundColor Yellow
$allChecksPassed = $false $allChecksPassed = $false
} }
} else { } else {
Write-Host "[FAIL] Expected commits from feature-api not found" -ForegroundColor Red Write-Host "[FAIL] app.py not found" -ForegroundColor Red
Write-Host "[HINT] Did you run setup.ps1?" -ForegroundColor Yellow
$allChecksPassed = $false $allChecksPassed = $false
} }
# Check if api-routes.py exists (should be on main after merge) # Check for merge commit (indicates three-way merge happened)
if (Test-Path "api-routes.py") {
Write-Host "[PASS] File 'api-routes.py' exists on main (from feature-api merge)" -ForegroundColor Green
} else {
Write-Host "[FAIL] File 'api-routes.py' not found on main" -ForegroundColor Red
Write-Host "[HINT] This file should appear after merging feature-api" -ForegroundColor Yellow
$allChecksPassed = $false
}
# Check if feature-ui branch exists
$featureUiBranch = git branch --list "feature-ui" 2>$null
if ($featureUiBranch) {
Write-Host "[PASS] Branch 'feature-ui' exists" -ForegroundColor Green
# Check if feature-ui has commits
$uiCommitCount = git rev-list main..feature-ui --count 2>$null
if ($uiCommitCount -gt 0) {
Write-Host "[INFO] Branch 'feature-ui' has $uiCommitCount commit(s)" -ForegroundColor Cyan
}
} else {
Write-Host "[FAIL] Branch 'feature-ui' not found" -ForegroundColor Red
Write-Host "[HINT] Create feature-ui with: git checkout -b feature-ui" -ForegroundColor Yellow
$allChecksPassed = $false
}
# Check for merge commits (indicates three-way merge happened)
$mergeCommits = git log --merges --oneline 2>$null $mergeCommits = git log --merges --oneline 2>$null
if ($mergeCommits) { if ($mergeCommits) {
Write-Host "[PASS] Merge commit(s) found (three-way merge completed)" -ForegroundColor Green Write-Host "[PASS] Merge commit found (three-way merge completed)" -ForegroundColor Green
# Get the merge commit message
$mergeMessage = git log --merges --format=%s -1 2>$null
Write-Host "[INFO] Merge commit message: '$mergeMessage'" -ForegroundColor Cyan
} else { } else {
Write-Host "[FAIL] No merge commits found" -ForegroundColor Red Write-Host "[FAIL] No merge commit found" -ForegroundColor Red
Write-Host "[HINT] Create divergence: make commits on both main and feature-ui, then merge" -ForegroundColor Yellow Write-Host "[HINT] Merge feature-login into main with: git merge feature-login" -ForegroundColor Yellow
$allChecksPassed = $false $allChecksPassed = $false
} }
# Check if ui.py exists (should be on main after merge) # Check if both branches contributed commits (true three-way merge)
if (Test-Path "ui.py") { $totalCommits = git rev-list --count HEAD 2>$null
Write-Host "[PASS] File 'ui.py' exists on main (from feature-ui merge)" -ForegroundColor Green if ($totalCommits -ge 4) {
Write-Host "[PASS] Repository has $totalCommits commits (branches diverged properly)" -ForegroundColor Green
} else { } else {
Write-Host "[FAIL] File 'ui.py' not found on main" -ForegroundColor Red Write-Host "[WARN] Repository has only $totalCommits commits (expected at least 4)" -ForegroundColor Yellow
Write-Host "[HINT] Create ui.py on feature-ui branch and merge it into main" -ForegroundColor Yellow Write-Host "[INFO] This might still be correct if you deleted commits" -ForegroundColor Cyan
$allChecksPassed = $false
} }
Set-Location .. Set-Location ..
@@ -124,13 +105,14 @@ if ($allChecksPassed) {
Write-Host "=====================================" -ForegroundColor Green Write-Host "=====================================" -ForegroundColor Green
Write-Host " CONGRATULATIONS! CHALLENGE PASSED!" -ForegroundColor Green Write-Host " CONGRATULATIONS! CHALLENGE PASSED!" -ForegroundColor Green
Write-Host "=====================================" -ForegroundColor Green Write-Host "=====================================" -ForegroundColor Green
Write-Host "`nYou've successfully learned about Git merging!" -ForegroundColor Cyan Write-Host "`nYou've successfully completed your first branch merge!" -ForegroundColor Cyan
Write-Host "You now understand:" -ForegroundColor Cyan Write-Host "You now understand:" -ForegroundColor Cyan
Write-Host " - Fast-forward merges (when main hasn't changed)" -ForegroundColor White Write-Host " - How to merge branches with git merge" -ForegroundColor White
Write-Host " - Three-way merges (when branches have diverged)" -ForegroundColor White Write-Host " - What a merge commit is and why it's created" -ForegroundColor White
Write-Host " - How to use git merge to combine branches" -ForegroundColor White Write-Host " - How divergent branches are combined" -ForegroundColor White
Write-Host " - Merge commits and how to view them" -ForegroundColor White Write-Host " - How to visualize merges with git log --graph" -ForegroundColor White
Write-Host "`nReady for the next module!" -ForegroundColor Green Write-Host "`nNext up: Module 05 - Merge Conflicts!" -ForegroundColor Yellow
Write-Host "You'll learn what happens when Git can't automatically merge." -ForegroundColor Cyan
Write-Host "" Write-Host ""
} else { } else {
Write-Host "`n[SUMMARY] Some checks failed. Review the hints above and try again." -ForegroundColor Red Write-Host "`n[SUMMARY] Some checks failed. Review the hints above and try again." -ForegroundColor Red

View File

@@ -1,4 +1,4 @@
# Module 06: Merge Conflicts # Module 05: Merge Conflicts
## Learning Objectives ## Learning Objectives
@@ -6,101 +6,476 @@ By the end of this module, you will:
- Understand what merge conflicts are and why they occur - Understand what merge conflicts are and why they occur
- Identify merge conflicts in your repository - Identify merge conflicts in your repository
- Read and interpret conflict markers - Read and interpret conflict markers
- Resolve merge conflicts manually - Resolve merge conflicts manually step-by-step
- Complete a merge after resolving conflicts - Complete a merge after resolving conflicts
## Challenge Description ## Challenge
### Setup
Run the setup script to create your challenge environment:
```powershell
.\setup.ps1
```
This will create a `challenge/` directory with a repository that has a merge conflict waiting to happen!
### Your Task
You have a repository with a main branch and a feature branch called `update-config`. Both branches have modified the same configuration file in different ways, creating a merge conflict. You have a repository with a main branch and a feature branch called `update-config`. Both branches have modified the same configuration file in different ways, creating a merge conflict.
Your task is to: **Your mission:**
1. Attempt to merge the `update-config` branch into `main` 1. Attempt to merge the `update-config` branch into `main`
2. Identify and understand the merge conflict 2. Git will tell you there's a conflict - don't panic!
3. Resolve the conflict by keeping both the timeout setting from `main` AND the debug setting from `update-config` 3. Resolve the conflict by keeping BOTH settings (timeout AND debug)
4. Complete the merge with an appropriate commit message 4. Complete the merge
## Key Concepts **Steps to follow:**
### What Are Merge Conflicts? 1. Navigate to the challenge directory: `cd challenge`
2. Make sure you're on main: `git branch`
3. Try to merge: `git merge update-config`
4. Git will report a conflict!
5. Open `config.py` in your text editor
6. Follow the step-by-step guide below to resolve it
7. Save the file, stage it, and commit
A merge conflict occurs when Git cannot automatically combine changes from two branches because they modify the same part of the same file in different ways. When this happens, Git pauses the merge and asks you to manually resolve the conflict. ## What Are Merge Conflicts?
### Conflict Markers A **merge conflict** occurs when Git cannot automatically combine changes because both branches modified the same lines in the same file.
When a conflict occurs, Git adds special markers to the affected files:
**Example scenario:**
``` ```
<<<<<<< HEAD main branch changes line 5: TIMEOUT = 30
Your current branch's changes feature branch changes line 5: DEBUG = True
=======
The incoming branch's changes
>>>>>>> branch-name
``` ```
- `<<<<<<< HEAD`: Marks the beginning of your current branch's version Git doesn't know which one you want! So it asks you to decide.
- `=======`: Separates the two versions
- `>>>>>>> branch-name`: Marks the end of the incoming branch's version
### Resolving Conflicts **When do conflicts happen?**
- ✅ Two branches modify the same line(s)
- ✅ One branch deletes a file that another branch modifies
- ❌ Different files are changed (no conflict!)
- ❌ Different lines in the same file are changed (no conflict!)
To resolve a conflict: ## Step-by-Step: Resolving Your First Conflict
1. Open the conflicted file in your editor
2. Look for the conflict markers
3. Decide which changes to keep (you can keep one side, the other, or combine both)
4. Remove the conflict markers
5. Stage the resolved file with `git add`
6. Complete the merge with `git commit`
## Useful Commands ### Step 1: Attempt the Merge
```bash ```bash
# Merge a branch (may result in conflicts) cd challenge
git merge <branch-name> git merge update-config
# Check the status during a conflict
git status
# See which files have conflicts
git diff --name-only --diff-filter=U
# View the conflict in detail
git diff
# After resolving, stage the file
git add <file>
# Complete the merge
git commit
# If you want to abort the merge
git merge --abort
# Visualize the branch history
git log --oneline --graph --all
``` ```
**You'll see:**
```
Auto-merging config.py
CONFLICT (content): Merge conflict in config.py
Automatic merge failed; fix conflicts and then commit the result.
```
**Don't panic!** This is normal. Git is just asking for your help.
---
### Step 2: Check What Happened
```bash
git status
```
**You'll see:**
```
On branch main
You have unmerged paths.
(fix conflicts and run "git commit")
(use "git merge --abort" to abort the merge)
Unmerged paths:
(use "git add <file>..." to mark resolution)
both modified: config.py
```
This tells you that `config.py` needs your attention!
---
### Step 3: Open the Conflicted File
Open `config.py` in your text editor. You'll see something like this:
```python
# config.py - Application configuration
APP_NAME = "My Application"
VERSION = "1.0.0"
<<<<<<< HEAD
TIMEOUT = 30
=======
DEBUG = True
>>>>>>> update-config
```
**Let's break down what you're seeing:**
---
## Understanding Conflict Markers
Git has added special markers to show you both versions:
```python
<<<<<<< HEAD Start of YOUR version (current branch)
TIMEOUT = 30 What YOU have on main
======= Divider between versions
DEBUG = True What THEY have on update-config
>>>>>>> update-config End of THEIR version
```
**The three parts:**
1. **`<<<<<<< HEAD`** - Everything between here and `=======` is YOUR current branch's version
2. **`=======`** - This separates the two versions
3. **`>>>>>>> update-config`** - Everything between `=======` and here is the INCOMING branch's version
---
## Step 4: Decide What to Keep
You have three options:
**Option A: Keep YOUR version (main)**
```python
# config.py - Application configuration
APP_NAME = "My Application"
VERSION = "1.0.0"
TIMEOUT = 30
```
**Option B: Keep THEIR version (update-config)**
```python
# config.py - Application configuration
APP_NAME = "My Application"
VERSION = "1.0.0"
DEBUG = True
```
**Option C: Keep BOTH (this is what we want!)**
```python
# config.py - Application configuration
APP_NAME = "My Application"
VERSION = "1.0.0"
TIMEOUT = 30
DEBUG = True
```
---
## Step 5: Edit the File to Resolve the Conflict
**What the file looks like NOW (with conflict markers):**
```python
# config.py - Application configuration
APP_NAME = "My Application"
VERSION = "1.0.0"
<<<<<<< HEAD
TIMEOUT = 30
=======
DEBUG = True
>>>>>>> update-config
```
**What you need to do:**
1. **Delete** the line `<<<<<<< HEAD`
2. **Keep** the line `TIMEOUT = 30`
3. **Delete** the line `=======`
4. **Keep** the line `DEBUG = True`
5. **Delete** the line `>>>>>>> update-config`
**What the file should look like AFTER (conflict resolved):**
```python
# config.py - Application configuration
APP_NAME = "My Application"
VERSION = "1.0.0"
TIMEOUT = 30
DEBUG = True
```
**Save the file!**
---
## Step 6: Mark the Conflict as Resolved
Tell Git you've fixed the conflict by staging the file:
```bash
git add config.py
```
This tells Git: "I've resolved the conflict in this file, it's ready to be committed."
---
## Step 7: Check Your Status
```bash
git status
```
**You should see:**
```
On branch main
All conflicts fixed but you are still merging.
(use "git commit" to conclude merge)
Changes to be committed:
modified: config.py
```
Great! Git knows the conflict is resolved and is ready for you to complete the merge.
---
## Step 8: Complete the Merge
```bash
git commit
```
Git will open your editor with a default merge message:
```
Merge branch 'update-config'
# Conflicts:
# config.py
```
You can keep this message or customize it. Save and close the editor.
**Or use a one-liner:**
```bash
git commit -m "Merge update-config: resolve config conflict"
```
---
## Step 9: Verify the Merge
```bash
git log --oneline --graph
```
You should see your merge commit!
```bash
cat config.py
```
Verify the file has BOTH settings and NO conflict markers!
---
## Visual Summary: Before and After
### Before Resolution (WRONG ❌)
```python
# config.py
APP_NAME = "My Application"
VERSION = "1.0.0"
<<<<<<< HEAD
TIMEOUT = 30
=======
DEBUG = True
>>>>>>> update-config
```
**This will NOT run! Conflict markers are syntax errors!**
### After Resolution (CORRECT ✅)
```python
# config.py
APP_NAME = "My Application"
VERSION = "1.0.0"
TIMEOUT = 30
DEBUG = True
```
**Clean code, no markers, both settings preserved!**
---
## Common Mistakes to Avoid
### ❌ Mistake 1: Forgetting to Remove Conflict Markers
```python
TIMEOUT = 30
======= Still has conflict marker!
DEBUG = True
```
**This is invalid Python code and will crash!**
### ❌ Mistake 2: Committing Without Staging
```bash
# Wrong order:
git commit # Error! File not staged
# Correct order:
git add config.py # Stage first
git commit # Then commit
```
### ❌ Mistake 3: Only Keeping One Side When You Need Both
```python
# If the task asks for BOTH settings, this is wrong:
TIMEOUT = 30
# Missing DEBUG = True
```
---
## Useful Commands Reference
```bash
# During a conflict
git status # See which files have conflicts
git diff # See the conflict in detail
# Resolving
git add <file> # Mark file as resolved
git commit # Complete the merge
# If you want to start over
git merge --abort # Cancel the merge, go back to before
# After resolving
git log --oneline --graph # See the merge commit
```
---
## Real-World Conflict Resolution Workflow
**Here's what you'll do every time you have a conflict:**
```bash
# 1. Attempt the merge
git merge feature-branch
# → Git says: "CONFLICT! Fix it!"
# 2. Check what's wrong
git status
# → Shows which files have conflicts
# 3. Open the file, find the markers
# <<<<<<< HEAD
# your version
# =======
# their version
# >>>>>>> branch
# 4. Edit the file to resolve
# Remove the markers, keep what you need
# 5. Stage the resolved file
git add config.py
# 6. Complete the merge
git commit -m "Resolve conflict in config.py"
# 7. Celebrate! 🎉
```
---
## What If You Get Stuck?
### Option 1: Abort the Merge
```bash
git merge --abort
```
This puts everything back to how it was before the merge. You can try again!
### Option 2: Ask for Help
```bash
git status # See what's going on
git diff # See the conflict details
```
---
## Pro Tips
**Use `git status` constantly** - It tells you exactly what to do next
**Search for `<<<<<<<`** - Your editor's search function can find all conflicts quickly
**Test after resolving** - Make sure your code still works!
**Read both sides carefully** - Sometimes you need parts from each version
**Take your time** - Conflicts are not a race. Understand what changed and why.
---
## Verification ## Verification
Run the verification script to check your solution: Once you've resolved the conflict and completed the merge, verify your solution:
```bash ```powershell
.\verify.ps1 .\verify.ps1
``` ```
The verification will check that: The verification will check that:
- The merge conflict was resolved - The merge conflict was resolved
- The merge was completed successfully - The merge was completed successfully
- Both configuration settings were preserved correctly - ✅ BOTH settings are in the config file (TIMEOUT and DEBUG)
- The commit history shows the merge - ✅ NO conflict markers remain
- ✅ The merge commit exists in history
## Tips ---
- Don't panic when you see a conflict - it's a normal part of working with Git ## Need to Start Over?
- Read the conflict markers carefully to understand both versions
- You can use `git status` to see which files have conflicts
- Always test your code after resolving conflicts to ensure it still works
- The conflict markers (`<<<<<<<`, `=======`, `>>>>>>>`) must be completely removed from the final file
## What You'll Learn If you want to reset the challenge and start fresh:
Merge conflicts are inevitable when working on a team or even when working alone across multiple branches. Learning to resolve them confidently is an essential Git skill. This module teaches you the fundamentals of conflict resolution that you'll use throughout your development career. ```powershell
.\reset.ps1
```
---
## What You've Learned
🎉 **Congratulations!** You can now:
- Recognize when a merge conflict occurs
- Read and understand conflict markers
- Edit files to resolve conflicts
- Stage resolved files with `git add`
- Complete a merge with `git commit`
**This is a critical skill!** Every developer encounters merge conflicts. Now you know exactly what to do when they happen.
---
## Quick Reference Card
**When you see a conflict:**
1. **Don't panic**
2. **Run `git status`** to see which files
3. **Open the file** in your editor
4. **Find the markers** (`<<<<<<<`, `=======`, `>>>>>>>`)
5. **Decide what to keep** (one side, other side, or both)
6. **Remove ALL markers** (the `<<<<<<<`, `=======`, `>>>>>>>` lines)
7. **Save the file**
8. **Stage it** (`git add filename`)
9. **Commit** (`git commit`)
10. **Verify** (`git status`, `git log --oneline --graph`)
**Remember:** The conflict markers are NOT valid code - they MUST be removed!

View File

@@ -60,7 +60,7 @@ git add config.json
git commit -m "Add timeout configuration" | Out-Null git commit -m "Add timeout configuration" | Out-Null
# Switch to feature branch: Add debug setting (conflicting change) # Switch to feature branch: Add debug setting (conflicting change)
git checkout update-config | Out-Null git switch update-config | Out-Null
$featureConfig = @" $featureConfig = @"
{ {
@@ -78,7 +78,7 @@ git add config.json
git commit -m "Add debug mode configuration" | Out-Null git commit -m "Add debug mode configuration" | Out-Null
# Switch back to main branch # Switch back to main branch
git checkout main | Out-Null git switch main | Out-Null
# Return to module directory # Return to module directory
Set-Location .. Set-Location ..

View File

@@ -128,7 +128,8 @@ if ($parentCount -ne 2) {
} }
# Check that both branches are merged # Check that both branches are merged
$branches = git branch --merged 2>$null $branches = git branch --merged update-config 2>$null
Write-Host "$branches $($branches -notmatch 'update-config')"
if ($branches -notmatch "update-config") { if ($branches -notmatch "update-config") {
Write-Host "[FAIL] The 'update-config' branch was not merged." -ForegroundColor Red Write-Host "[FAIL] The 'update-config' branch was not merged." -ForegroundColor Red
Set-Location .. Set-Location ..

View File

@@ -0,0 +1,904 @@
# Facilitator Setup Guide - The Great Print Project
This guide helps workshop facilitators set up the cloud-based multiplayer Git module.
## Overview
The Great Print Project is a collaborative Git exercise where pairs of students work together on a shared repository hosted on your Gitea server at **https://git.frod.dk/multiplayer**.
**What participants will do:**
- Clone a real repository from your server
- Collaborate with partners on shared branches
- Deliberately create and resolve merge conflicts
- Create pull requests and review code
- Experience the full collaborative Git workflow
---
## Prerequisites
### Gitea Server Setup
You should have:
- Gitea running at https://git.frod.dk/multiplayer
- Admin access to create repositories and users
- HTTPS or SSH access enabled for Git operations
**Need to set up Gitea?** See the main workshop's `GITEA-SETUP.md` for Docker + Cloudflare Tunnel instructions.
### Workshop Materials
Participants need:
- Access to the module README in `01_essentials/09-multiplayer/README.md`
- Git installed (version 2.23+)
- Python 3.6+ (to run the print project)
- Text editor
---
## Pre-Workshop Setup
### Step 1: Create User Accounts
Create individual Gitea accounts for each participant.
**Recommended naming:**
- `student01`, `student02`, `student03`, etc.
- Or use their real names/emails if preferred
**Two approaches:**
**Option A: Manual account creation**
1. Go to Gitea admin panel
2. Create users one by one
3. Set initial passwords (students can change later)
4. Provide credentials to students
**Option B: Self-registration** (if you trust your network)
1. Enable self-registration in Gitea settings
2. Provide registration URL to students
3. They create their own accounts
4. You verify and approve accounts
**Access tokens (recommended for HTTPS):**
- Have students create personal access tokens after logging in
- Settings → Applications → Generate New Token
- Token needs `repo` scope
- Students use token as password when pushing/pulling
### Step 2: Create the Repository
Create the shared repository: **great-print-project**
**Via Gitea web UI:**
1. Log in as admin or organization account
2. Click "+" → "New Repository"
3. **Name:** `great-print-project`
4. **Owner:** `multiplayer` (organization) or your admin account
5. **Visibility:** Private (only visible to students you add)
6. **Initialize:** Check "Initialize this repository with selected files"
7. **README:** Yes
8. **License:** None
9. **.gitignore:** Python
10. Click "Create Repository"
**Via command line (alternative):**
```bash
# Create local directory
mkdir great-print-project
cd great-print-project
# Initialize git
git init
# Add files (see Step 3)
git add .
git commit -m "Initial commit: The Great Print Project"
# Create bare repo on server
ssh user@git.frod.dk
cd /path/to/gitea/repositories/multiplayer
git init --bare great-print-project.git
exit
# Push to server
git remote add origin git@git.frod.dk:multiplayer/great-print-project.git
git push -u origin main
```
### Step 3: Add Starter Code to Repository
Commit these four files to the repository:
#### File 1: main.py
```python
#!/usr/bin/env python3
"""
The Great Print Project
A collaborative Git exercise
When everyone completes their assigned functions,
this program will print the complete alphabet and numbers!
"""
from letters import print_letters
from numbers import print_numbers
def main():
print("=" * 50)
print(" THE GREAT PRINT PROJECT")
print("=" * 50)
print("\nLetters:")
print_letters()
print() # New line after letters
print("\nNumbers:")
print_numbers()
print() # New line after numbers
print("\n" + "=" * 50)
print(" PROJECT COMPLETE!")
print("=" * 50)
if __name__ == "__main__":
main()
```
#### File 2: letters.py
```python
"""
Letter Printing Functions
Each pair completes their assigned functions.
Expected output: A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
"""
def print_a():
"""Print letter A - EXAMPLE (already completed)"""
print("A", end=" ")
def print_b():
# TODO: Pair 1 - implement this function
pass
def print_c():
# TODO: Pair 1 - implement this function
pass
def print_d():
# TODO: Pair 1 - implement this function
pass
def print_e():
# TODO: Pair 2 - implement this function
pass
def print_f():
# TODO: Pair 2 - implement this function
pass
def print_g():
# TODO: Pair 2 - implement this function
pass
def print_h():
# TODO: Pair 3 - implement this function
pass
def print_i():
# TODO: Pair 3 - implement this function
pass
def print_j():
# TODO: Pair 3 - implement this function
pass
def print_k():
# TODO: Pair 4 - implement this function
pass
def print_l():
# TODO: Pair 4 - implement this function
pass
def print_m():
# TODO: Pair 4 - implement this function
pass
def print_n():
# TODO: Pair 5 - implement this function
pass
def print_o():
# TODO: Pair 5 - implement this function
pass
def print_p():
# TODO: Pair 5 - implement this function
pass
def print_q():
# TODO: Pair 6 - implement this function
pass
def print_r():
# TODO: Pair 6 - implement this function
pass
def print_s():
# TODO: Pair 6 - implement this function
pass
def print_t():
# TODO: Pair 7 - implement this function
pass
def print_u():
# TODO: Pair 7 - implement this function
pass
def print_v():
# TODO: Pair 7 - implement this function
pass
def print_w():
# TODO: Pair 8 - implement this function
pass
def print_x():
# TODO: Pair 8 - implement this function
pass
def print_y():
# TODO: Pair 8 - implement this function
pass
def print_z():
# TODO: Pair 9 - implement this function
pass
def print_letters():
"""Print all letters A-Z"""
print_a()
print_b()
print_c()
print_d()
print_e()
print_f()
print_g()
print_h()
print_i()
print_j()
print_k()
print_l()
print_m()
print_n()
print_o()
print_p()
print_q()
print_r()
print_s()
print_t()
print_u()
print_v()
print_w()
print_x()
print_y()
print_z()
```
#### File 3: numbers.py
```python
"""
Number Printing Functions
Each pair completes their assigned functions.
Expected output: 0 1 2 3 4 5 6 7 8 9
"""
def print_0():
# TODO: Pair 9 - implement this function
pass
def print_1():
# TODO: Pair 10 - implement this function
pass
def print_2():
# TODO: Pair 10 - implement this function
pass
def print_3():
# TODO: Pair 10 - implement this function
pass
def print_4():
# TODO: Pair 11 - implement this function
pass
def print_5():
# TODO: Pair 11 - implement this function
pass
def print_6():
# TODO: Pair 11 - implement this function
pass
def print_7():
# TODO: Pair 12 - implement this function
pass
def print_8():
# TODO: Pair 12 - implement this function
pass
def print_9():
# TODO: Pair 12 - implement this function
pass
def print_numbers():
"""Print all numbers 0-9"""
print_0()
print_1()
print_2()
print_3()
print_4()
print_5()
print_6()
print_7()
print_8()
print_9()
```
#### File 4: assignments.md
```markdown
# Pair Assignments
## How This Works
Each pair is assigned 3 functions to implement. You'll work together on a shared branch.
**Important:** Check with your facilitator for your pair number and assignment!
---
## Assignments
### Pair 1
- **Functions:** `print_b()`, `print_c()`, `print_d()`
- **File:** `letters.py`
- **Branch:** `pair-1-bcd`
### Pair 2
- **Functions:** `print_e()`, `print_f()`, `print_g()`
- **File:** `letters.py`
- **Branch:** `pair-2-efg`
### Pair 3
- **Functions:** `print_h()`, `print_i()`, `print_j()`
- **File:** `letters.py`
- **Branch:** `pair-3-hij`
### Pair 4
- **Functions:** `print_k()`, `print_l()`, `print_m()`
- **File:** `letters.py`
- **Branch:** `pair-4-klm`
### Pair 5
- **Functions:** `print_n()`, `print_o()`, `print_p()`
- **File:** `letters.py`
- **Branch:** `pair-5-nop`
### Pair 6
- **Functions:** `print_q()`, `print_r()`, `print_s()`
- **File:** `letters.py`
- **Branch:** `pair-6-qrs`
### Pair 7
- **Functions:** `print_t()`, `print_u()`, `print_v()`
- **File:** `letters.py`
- **Branch:** `pair-7-tuv`
### Pair 8
- **Functions:** `print_w()`, `print_x()`, `print_y()`
- **File:** `letters.py`
- **Branch:** `pair-8-wxy`
### Pair 9
- **Functions:** `print_z()`, `print_0()`, `print_1()`
- **Files:** `letters.py`, `numbers.py`
- **Branch:** `pair-9-z01`
### Pair 10
- **Functions:** `print_2()`, `print_3()`, `print_4()`
- **File:** `numbers.py`
- **Branch:** `pair-10-234`
### Pair 11
- **Functions:** `print_5()`, `print_6()`, `print_7()`
- **File:** `numbers.py`
- **Branch:** `pair-11-567`
### Pair 12
- **Functions:** `print_8()`, `print_9()`
- **File:** `numbers.py`
- **Branch:** `pair-12-89`
---
## Example Implementation
```python
def print_a():
"""Print letter A - EXAMPLE (already completed)"""
print("A", end=" ")
```
**Your functions should follow the same pattern:**
```python
def print_x():
"""Print letter/number X"""
print("X", end=" ")
```
---
## Testing
After implementing your functions, test with:
```bash
python main.py
```
You should see your letters/numbers in the output!
---
## Questions?
Ask your facilitator for:
- Your pair number
- Your Gitea credentials
- Help with authentication setup
- Any Git or Python issues
```
#### File 5: README.md (in repository)
```markdown
# The Great Print Project 🎯
A collaborative Git exercise for learning teamwork with version control!
## Goal
When everyone completes their assigned functions, running `python main.py` will print:
```
==================================================
THE GREAT PRINT PROJECT
==================================================
Letters:
A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
Numbers:
0 1 2 3 4 5 6 7 8 9
==================================================
PROJECT COMPLETE!
==================================================
```
## Your Mission
1. Find your pair assignment in `assignments.md`
2. Clone this repository
3. Create your feature branch
4. Implement your assigned functions
5. Practice collaboration: push, pull, resolve conflicts
6. Create a pull request
7. Celebrate when your code is merged!
## Quick Start
```bash
# Clone the repository
git clone https://git.frod.dk/multiplayer/great-print-project.git
cd great-print-project
# Check your assignment
cat assignments.md
# Create your branch (replace X with your pair number)
git switch -c pair-X-feature
# Edit your file (letters.py or numbers.py)
# Implement your functions
# Test it
python main.py
# Commit and push
git add .
git commit -m "Implement print_x() functions"
git push -u origin pair-X-feature
```
## Files
- **main.py** - Orchestrator (runs the whole program)
- **letters.py** - Functions for printing A-Z
- **numbers.py** - Functions for printing 0-9
- **assignments.md** - See which functions your pair should implement
## Need Help?
See the module README in the workshop repository for detailed step-by-step instructions!
**Happy Collaborating! 🚀**
```
**Commit these files:**
```bash
git add main.py letters.py numbers.py assignments.md README.md
git commit -m "Add Great Print Project starter code"
git push origin main
```
### Step 4: Grant Student Access
Add students as collaborators with write access:
**Via Gitea web UI:**
1. Go to repository → Settings → Collaborators
2. Add each student account
3. Set permission level: **Write** (allows push, pull, branch creation)
**Important:** Students need **Write** access to:
- Create branches
- Push commits
- Create pull requests
### Step 5: Configure Branch Protection (Optional)
To prevent accidental pushes to main:
1. Repository → Settings → Branches
2. Add protection rule for `main` branch
3. Settings:
- **Block direct pushes:** Yes (requires pull requests)
- **Require PR reviews:** Optional (you can review PRs yourself)
- **Auto-merge:** Disabled (you merge manually or students do)
This ensures students:
- MUST use feature branches
- MUST create pull requests
- Can't accidentally break main
**For beginners:** Consider allowing students to merge their own PRs after approval to complete the full workflow.
### Step 6: Test the Setup
Before the workshop, test as a student would:
```bash
# Clone as a test student
git clone https://git.frod.dk/multiplayer/great-print-project.git
cd great-print-project
# Run the program
python main.py
# Should show only "A" with missing letters/numbers
# Create test branch
git switch -c test-branch
# Edit letters.py, add print_b()
def print_b():
print("B", end=" ")
# Commit and push
git add letters.py
git commit -m "Test commit"
git push -u origin test-branch
# Create test pull request
# (Do this via web UI)
# Clean up test branch after
git push origin --delete test-branch
```
---
## During the Workshop
### Pairing Students
**Strategies for assigning pairs:**
**Option 1: Random pairing**
- Use a random number generator
- Pair students as they arrive
**Option 2: Skill-based pairing**
- Mix experienced and beginner students
- Balance pair capabilities
**Option 3: Let them choose**
- Students pick their own partners
- Good for building team dynamics
**Announce pairs clearly:**
- Write on board/screen: "Pair 1: Alice & Bob"
- Provide printed assignment sheet
- Update `assignments.md` in repo if needed
### Timeline
**Suggested schedule for 2-hour session:**
- **0:00-0:10** (10 min): Introduction, distribute credentials
- **0:10-0:20** (10 min): Students clone repo, verify access
- **0:20-0:35** (15 min): Part 1 - Getting Started
- **0:35-0:55** (20 min): Part 2 - First Contribution
- **0:55-1:25** (30 min): Part 3 - Conflict Exercise (key learning!)
- **1:25-1:45** (20 min): Part 4 - Pull Requests
- **1:45-2:00** (15 min): Part 5 - Syncing, Q&A, wrap-up
### Monitoring Progress
**Use Gitea to track:**
1. **Branches created:** Repository → Branches
- Should see `pair-1-bcd`, `pair-2-efg`, etc.
2. **Commits:** Repository → Commits
- Each pair should have multiple commits
3. **Pull requests:** Repository → Pull Requests
- Should see one PR per pair
**Walk around the room:**
- Check screens for conflict markers
- Ask pairs how they're resolving conflicts
- Ensure both partners are engaged
**Common issues to watch for:**
- Partners not using the same branch name
- Forgetting to pull before pushing
- Not removing conflict markers completely
- Committing to main instead of feature branch
### Managing Pull Requests
**Your role:**
**Option A: Review and merge yourself**
- Teaches students what good reviews look like
- Ensures quality before merging
- More facilitator work
**Option B: Students merge their own**
- More autonomous learning
- Students experience complete workflow
- Risk of messy main branch
**Recommended approach:**
1. First 2-3 PRs: You review and merge (demonstrate good practices)
2. Remaining PRs: Students review each other, you approve
3. Students can merge after approval
**What to check in PR reviews:**
- Functions implemented correctly
- No conflict markers in code
- Code follows pattern (e.g., `print("X", end=" ")`)
- Meaningful commit messages
### Handling Problems
**Common issues and solutions:**
**Problem: "I can't push!"**
- Check they're authenticated (token or SSH key)
- Check they pulled latest changes first
- Check branch name matches their partner's
**Problem: "Merge conflict won't resolve!"**
- Walk through Part 3 step-by-step with them
- Show them the conflict markers
- Verify they removed ALL markers
- Run `python main.py` together to test
**Problem: "We both committed to main!"**
- Have them create proper feature branch
- Use `git cherry-pick` to move commits
- Reset main to origin/main
**Problem: "GitHub Desktop / GUI tool shows something different"**
- Recommend command line for this exercise
- GUIs can hide important details during conflicts
### Celebrating Success
When all pairs have merged:
```bash
git pull origin main
python main.py
```
**Everyone should see:**
```
==================================================
THE GREAT PRINT PROJECT
==================================================
Letters:
A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
Numbers:
0 1 2 3 4 5 6 7 8 9
==================================================
PROJECT COMPLETE!
==================================================
```
**Take a screenshot!** Share it with the class. This is a genuine collaborative achievement.
---
## Post-Workshop
### Cleanup (Optional)
**Keep the repository for future workshops:**
- Delete all feature branches: `git push origin --delete pair-1-bcd` (etc.)
- Reset main to initial state
- Reuse for next cohort
**Archive the session:**
- Export final repository state
- Take screenshots of successful PRs
- Save for portfolio/examples
### Student Takeaways
Provide students:
- Link to repository (they can clone for reference)
- Completion certificate (if applicable)
- Next steps: contributing to open source, Git resources
---
## Troubleshooting
### Gitea Server Issues
**Problem: Server unreachable**
- Check Cloudflare Tunnel is running: `cloudflared tunnel info`
- Verify Gitea container is up: `docker ps`
- Check firewall rules
**Problem: SSH not working**
- Verify SSH port is exposed in docker-compose.yml
- Check Cloudflare Tunnel config includes SSH
- Test: `ssh -T git@git.frod.dk`
**Problem: HTTPS clone fails**
- Check certificate validity
- Try `GIT_SSL_NO_VERIFY=true git clone ...` (temporary workaround)
- Configure Gitea to use proper HTTPS certificates
### Authentication Issues
**Problem: Students can't log in**
- Verify accounts created and active
- Reset passwords if needed
- Check email verification isn't blocking (disable for workshop)
**Problem: Push fails with authentication error**
- HTTPS: Ensure students use access token, not password
- SSH: Verify keys added to Gitea account
- Check repo permissions (must be Write, not Read)
### Git Workflow Issues
**Problem: Students create PR but can't merge**
- Check branch protection rules
- Verify they have Write access
- Ensure PR doesn't have conflicts
**Problem: Main branch gets messy**
- Reset to last good commit: `git reset --hard <commit>`
- Force push: `git push --force origin main` (CAREFUL!)
- Or start fresh: delete repo, recreate with starter code
---
## Tips for Success
### Before Workshop
- Test the entire flow yourself as a student
- Prepare credential sheets for each student
- Have backup plan if server goes down (local git exercise)
- Prepare slides explaining merge conflicts visually
### During Workshop
- **Start on time** - respect everyone's schedule
- **Pair programming** - ensure both partners engage
- **Encourage talking** - best conflicts are resolved by discussion
- **Celebrate small wins** - first push, first conflict resolution
- **Walk the room** - see screens, answer questions live
### After Workshop
- Gather feedback - what worked, what didn't
- Note timing - were parts too rushed or too slow?
- Archive successful PRs as examples
- Plan improvements for next session
---
## Scaling Considerations
### Small Groups (4-8 students, 2-4 pairs)
- More hands-on facilitator time
- Can review all PRs in detail
- Easier to monitor progress
### Medium Groups (10-20 students, 5-10 pairs)
- Recommended size
- Good mix of collaboration and individual attention
- Helps if you have a teaching assistant
### Large Groups (20+ students, 10+ pairs)
- Consider multiple repositories (split into groups of 12 pairs max)
- Recruit teaching assistants to help monitor
- Use breakout rooms (if online)
- Automate more (less PR review, more self-merging)
---
## Additional Resources
### For You (Facilitator)
- Gitea documentation: https://docs.gitea.io/
- Pro Git book (free): https://git-scm.com/book/en/v2
- Teaching Git: https://git-scm.com/doc
### For Students
- Git cheatsheet (included in workshop repo)
- Interactive Git tutorial: https://learngitbranching.js.org/
- Oh Shit Git: https://ohshitgit.com/ (recovering from mistakes)
---
## Questions or Issues?
This guide should cover most scenarios. If you encounter issues not listed here:
1. Check Gitea logs: `docker logs gitea-container-name`
2. Test with minimal setup (single test student)
3. Consult Gitea documentation
4. Reach out to workshop repository maintainers
**Good luck with your workshop! The multiplayer module is where Git skills really come alive.**

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,448 @@
# Module 06: Merge Strategies - Fast-Forward vs Three-Way
## Learning Objectives
In this module, you will:
- Understand the difference between fast-forward and three-way merges
- Learn when Git automatically chooses each strategy
- Force specific merge behavior with `--no-ff` and `--ff-only` flags
- Understand the trade-offs between linear and branched history
- Make informed decisions about merge strategies for different workflows
## Prerequisites
Before starting this module, you should have completed:
- Module 03: Branching Basics
- Module 04: Merging Branches
## Challenge
### Setup
Run the setup script to create your challenge environment:
```powershell
.\setup.ps1
```
This will create a `challenge/` directory with scenarios for both fast-forward and three-way merges.
### Your Task
You'll experiment with both types of merges and learn how to control Git's merge behavior.
**Part 1: Fast-Forward Merge**
1. Merge `feature-fast-forward` into main
2. Observe Git's "Fast-forward" message
3. Examine the linear history
**Part 2: Three-Way Merge**
4. Merge `feature-divergent` into main
5. Observe Git creates a merge commit
6. Examine the branched history
**Part 3: Force Merge Commit**
7. Merge `feature-optional` into main using `--no-ff`
8. Compare with the fast-forward from Part 1
**Steps:**
1. Navigate to the challenge directory: `cd challenge`
2. View all branches: `git branch -a`
3. View the current graph: `git log --oneline --graph --all`
4. Merge feature-fast-forward: `git merge feature-fast-forward`
5. Check the log: `git log --oneline --graph`
6. Merge feature-divergent: `git merge feature-divergent`
7. Check the log: `git log --oneline --graph --all`
8. Merge feature-optional with --no-ff: `git merge --no-ff feature-optional`
9. Compare the results: `git log --oneline --graph --all`
> **Key Questions to Consider:**
> - Which merges created merge commits?
> - Which merge kept a linear history?
> - How does `--no-ff` change Git's behavior?
> - When would you prefer each approach?
## Understanding Merge Strategies
### Fast-Forward Merge
A **fast-forward merge** happens when the target branch (e.g., `main`) hasn't changed since the feature branch was created. Git simply "fast-forwards" the branch pointer to the latest commit on the feature branch.
**Before the merge:**
```
main: A---B
\
feature: C---D
```
In this scenario:
- Commit B is where `feature` branched off from `main`
- Commits C and D are new commits on the `feature` branch
- `main` has NO new commits since the branch split
- The history is **linear** (straight line from A to D)
**After `git merge feature` (on main):**
```
main: A---B---C---D
feature
```
**What happened:**
- Git moved the `main` pointer forward to commit D
- NO merge commit was created
- The history remains linear (a straight line)
- Both `main` and `feature` now point to the same commit (D)
**Command:**
```bash
git switch main
git merge feature-fast-forward
```
**Output you'll see:**
```
Updating abc123..def456
Fast-forward
new-feature.py | 10 ++++++++++
1 file changed, 10 insertions(+)
```
**Notice the "Fast-forward" message!**
**Characteristics:**
- ✅ Keeps history linear and clean
- ✅ Simpler to read in `git log`
- ✅ No extra merge commit
- ❌ Loses visibility that work was done on a branch
- ❌ Harder to revert entire features at once
---
### Three-Way Merge
A **three-way merge** happens when BOTH branches have new commits since they diverged. Git must combine changes from both branches, which creates a special merge commit.
**Before the merge:**
```
main: A---B---C---E
\
feature: D---F
```
In this scenario:
- Commit B is where `feature` branched off from `main`
- Commits C and E are new commits on `main`
- Commits D and F are new commits on `feature`
- The branches have **diverged** (both have unique commits)
**After `git merge feature` (on main):**
```
main: A---B---C---E---M
\ /
feature: D---F---/
```
**What happened:**
- Git created a new **merge commit** (M)
- Commit M has TWO parent commits: E (from main) and F (from feature)
- The merge commit combines changes from both branches
- The history shows the branches converging
**Why it's called "three-way":**
Git uses THREE commits to perform the merge:
1. **Commit B** - The common ancestor (where branches split)
2. **Commit E** - The latest commit on `main`
3. **Commit F** - The latest commit on `feature`
Git compares all three to figure out what changed on each branch and how to combine them.
**Command:**
```bash
git switch main
git merge feature-divergent
```
**Output you'll see:**
```
Merge made by the 'ort' strategy.
feature-code.py | 5 +++++
1 file changed, 5 insertions(+)
```
**Notice it says "Merge made by the 'ort' strategy" instead of "Fast-forward"!**
**Characteristics:**
- ✅ Preserves feature branch history
- ✅ Shows when features were merged
- ✅ Easier to revert entire features (revert the merge commit)
- ✅ Clear visualization in git log --graph
- ❌ Creates more commits (merge commits)
- ❌ History can become complex with many merges
---
### When Does Each Type Happen?
| Situation | Merge Type | Merge Commit? | Git's Behavior |
|-----------|------------|---------------|----------------|
| Target branch (main) has NO new commits | Fast-Forward | ❌ No | Automatic |
| Target branch (main) HAS new commits | Three-Way | ✅ Yes | Automatic |
| You use `--no-ff` flag | Three-Way | ✅ Yes | Forced |
| You use `--ff-only` flag | Fast-Forward | ❌ No | Fails if not possible |
**Git chooses automatically** based on the branch state, but you can override this behavior!
---
## Controlling Merge Behavior
### Force a Merge Commit with `--no-ff`
Even if a fast-forward is possible, you can force Git to create a merge commit:
```bash
git merge --no-ff feature-optional
```
**Before (fast-forward would be possible):**
```
main: A---B
\
feature: C---D
```
**After `git merge --no-ff feature` (on main):**
```
main: A---B-------M
\ /
feature: C---D
```
Notice that even though main didn't change, a merge commit (M) was created!
**When to use `--no-ff`:**
- ✅ When you want to preserve the feature branch in history
- ✅ For important features that might need to be reverted
- ✅ In team workflows where you want to see when features were merged
- ✅ When following a branching model like Git Flow
**Example:**
```bash
# You've finished a major feature on feature-auth
git switch main
git merge --no-ff feature-auth -m "Merge feature-auth: Add user authentication system"
```
This creates a clear marker in history showing when and what was merged.
---
### Require Fast-Forward with `--ff-only`
You can make Git fail the merge if a fast-forward isn't possible:
```bash
git merge --ff-only feature-branch
```
**What happens:**
- ✅ If fast-forward is possible, Git merges
- ❌ If branches have diverged, Git refuses to merge
**Output when it fails:**
```
fatal: Not possible to fast-forward, aborting.
```
**When to use `--ff-only`:**
- ✅ When you want to keep history strictly linear
- ✅ To ensure you rebase before merging
- ✅ In workflows that prohibit merge commits
**Example workflow:**
```bash
# Try to merge
git merge --ff-only feature-branch
# If it fails, rebase first
git switch feature-branch
git rebase main
git switch main
git merge --ff-only feature-branch # Now it works!
```
---
## Visualizing the Difference
### Linear History (Fast-Forward)
```bash
git log --oneline --graph
```
```
* d1e2f3g (HEAD -> main, feature) Add feature C
* a4b5c6d Add feature B
* 7e8f9g0 Add feature A
* 1a2b3c4 Initial commit
```
Notice the straight line! No branching visible.
---
### Branched History (Three-Way Merge)
```bash
git log --oneline --graph --all
```
```
* m1e2r3g (HEAD -> main) Merge branch 'feature'
|\
| * f4e5a6t (feature) Add feature implementation
| * u7r8e9s Feature setup
* | a1b2c3d Update documentation on main
|/
* 0i1n2i3t Initial commit
```
Notice the branching pattern! You can see:
- Where the branch split (`|/`)
- Commits on each branch (`|`)
- Where branches merged (`* merge commit`)
---
## Decision Guide: Which Strategy to Use?
### Use Fast-Forward When:
- ✅ Working on personal projects
- ✅ Want simplest, cleanest history
- ✅ Small changes or bug fixes
- ✅ Don't need to track feature branches
- ✅ History readability is top priority
### Use Three-Way Merge (or force with --no-ff) When:
- ✅ Working in teams
- ✅ Want to preserve feature context
- ✅ Need to revert features as units
- ✅ Following Git Flow or similar workflow
- ✅ Important to see when features were integrated
### Force Fast-Forward (--ff-only) When:
- ✅ Enforcing rebase workflow
- ✅ Maintaining strictly linear history
- ✅ Integration branch requires clean history
---
## Comparison Table
| Aspect | Fast-Forward | Three-Way |
|--------|-------------|-----------|
| **When it happens** | Target branch unchanged | Both branches have new commits |
| **Merge commit created?** | ❌ No | ✅ Yes |
| **History appearance** | Linear | Branched |
| **Command output** | "Fast-forward" | "Merge made by..." |
| **Git log --graph** | Straight line | Fork and merge pattern |
| **Can revert entire feature?** | ❌ No (must revert each commit) | ✅ Yes (revert merge commit) |
| **Force it** | `--ff-only` | `--no-ff` |
| **Best for** | Solo work, small changes | Team work, features |
---
## Useful Commands
### Merging with Strategy Control
```bash
git merge <branch> # Let Git decide automatically
git merge --no-ff <branch> # Force a merge commit
git merge --ff-only <branch> # Only merge if fast-forward possible
git merge --abort # Cancel a merge in progress
```
### Viewing Different Merge Types
```bash
# See all merges
git log --merges # Only merge commits
git log --no-merges # Hide merge commits
# Visualize history
git log --oneline --graph # See branch structure
git log --oneline --graph --all # Include all branches
git log --first-parent # Follow only main branch line
# Check if merge would be fast-forward
git merge-base main feature # Find common ancestor
git log main..feature # See commits unique to feature
```
### Configuring Default Behavior
```bash
# Disable fast-forward by default
git config merge.ff false # Always create merge commits
# Only allow fast-forward
git config merge.ff only # Refuse non-fast-forward merges
# Reset to default
git config --unset merge.ff
```
---
## Common Patterns in the Wild
### GitHub Flow (Simple)
- Use fast-forward when possible
- Short-lived feature branches
- Merge to main frequently
### Git Flow (Structured)
- Always use `--no-ff` for features
- Preserve branch history
- Complex release management
### Rebase Workflow
- Always use `--ff-only`
- Rebase before merging
- Strictly linear history
---
## Verification
Once you've completed all three merges, verify your solution:
```powershell
.\verify.ps1
```
The verification script will check that you've experienced both types of merges and used the `--no-ff` flag.
## 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.
## What's Next?
Now that you understand merge strategies, you can make informed decisions about your workflow. Consider:
- **For personal projects:** Fast-forward merges keep history simple
- **For team projects:** Three-way merges preserve context
- **For open source:** Follow the project's contribution guidelines
The best strategy depends on your team's needs and workflow!

View File

@@ -0,0 +1,24 @@
#!/usr/bin/env pwsh
<#
.SYNOPSIS
Resets the Module 06 challenge environment.
.DESCRIPTION
This script removes the challenge directory and re-runs the setup script
to give you a fresh start.
#>
Write-Host "`n=== Resetting Module 06 Challenge ===" -ForegroundColor Cyan
# Remove challenge directory if it exists
if (Test-Path "challenge") {
Write-Host "Removing existing challenge directory..." -ForegroundColor Yellow
Remove-Item -Recurse -Force "challenge"
Write-Host "Challenge directory removed." -ForegroundColor Green
} else {
Write-Host "No challenge directory found to remove." -ForegroundColor Yellow
}
# Run setup script
Write-Host "`nRunning setup script..." -ForegroundColor Cyan
& "./setup.ps1"

View File

@@ -0,0 +1,221 @@
#!/usr/bin/env pwsh
<#
.SYNOPSIS
Sets up the Module 06 challenge environment for learning merge strategies.
.DESCRIPTION
This script creates a challenge directory with a Git repository that
contains scenarios for both fast-forward and three-way merges, allowing
students to compare different merge strategies.
#>
Write-Host "`n=== Setting up Module 06 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"
# ========================================
# Scenario 1: Fast-Forward Merge Setup
# ========================================
Write-Host "Setting up fast-forward merge scenario..." -ForegroundColor Green
# Commit 1: Initial structure on main
$appContent = @"
# app.py - Main application
def main():
print("Application started")
pass
if __name__ == "__main__":
main()
"@
Set-Content -Path "app.py" -Value $appContent
git add .
git commit -m "Initial application structure" | Out-Null
# Create feature-fast-forward branch (main won't change after this)
git switch -c feature-fast-forward | Out-Null
# Commit on feature-fast-forward
$utilsContent = @"
# utils.py - Utility functions
def format_string(text):
"""Format a string to title case."""
return text.title()
def validate_input(text):
"""Validate user input."""
return text and len(text) > 0
"@
Set-Content -Path "utils.py" -Value $utilsContent
git add .
git commit -m "Add utility functions" | Out-Null
# Second commit on feature-fast-forward
$utilsContent = @"
# utils.py - Utility functions
def format_string(text):
"""Format a string to title case."""
return text.title()
def validate_input(text):
"""Validate user input."""
return text and len(text) > 0
def sanitize_input(text):
"""Remove dangerous characters from input."""
return text.replace("<", "").replace(">", "")
"@
Set-Content -Path "utils.py" -Value $utilsContent
git add .
git commit -m "Add input sanitization" | Out-Null
# ========================================
# Scenario 2: Three-Way Merge Setup
# ========================================
Write-Host "Setting up three-way merge scenario..." -ForegroundColor Green
# Switch back to main
git switch main | Out-Null
# Create feature-divergent branch
git switch -c feature-divergent | Out-Null
# Commit on feature-divergent
$authContent = @"
# auth.py - Authentication module
def authenticate(username, password):
"""Authenticate a user."""
print(f"Authenticating: {username}")
# TODO: Implement actual authentication
return True
"@
Set-Content -Path "auth.py" -Value $authContent
git add .
git commit -m "Add authentication module" | Out-Null
# Second commit on feature-divergent
$authContent = @"
# auth.py - Authentication module
def authenticate(username, password):
"""Authenticate a user."""
print(f"Authenticating: {username}")
# TODO: Implement actual authentication
return True
def check_permissions(user, resource):
"""Check if user has permission for resource."""
print(f"Checking permissions for {user}")
return True
"@
Set-Content -Path "auth.py" -Value $authContent
git add .
git commit -m "Add permission checking" | Out-Null
# Switch back to main and make a commit (creates divergence)
git switch main | Out-Null
$readmeContent = @"
# My Application
A Python application with utilities and authentication.
## Features
- String formatting and validation
- User authentication
- Permission management
## Setup
1. Install Python 3.8+
2. Run: python app.py
"@
Set-Content -Path "README.md" -Value $readmeContent
git add .
git commit -m "Add README documentation" | Out-Null
# ========================================
# Scenario 3: Optional Fast-Forward for --no-ff
# ========================================
Write-Host "Setting up --no-ff demonstration scenario..." -ForegroundColor Green
# Create feature-optional branch (main won't change after this)
git switch -c feature-optional | Out-Null
# Commit on feature-optional
$configContent = @"
# config.py - Configuration settings
DEBUG_MODE = False
LOG_LEVEL = "INFO"
DATABASE_URL = "sqlite:///app.db"
"@
Set-Content -Path "config.py" -Value $configContent
git add .
git commit -m "Add configuration module" | Out-Null
# Switch back to main
git switch main | 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 "`nYou have THREE scenarios set up:" -ForegroundColor Yellow
Write-Host "`n Scenario 1: Fast-Forward Merge" -ForegroundColor White
Write-Host " Branch: feature-fast-forward" -ForegroundColor Cyan
Write-Host " Status: main has NOT changed since branch was created" -ForegroundColor Cyan
Write-Host " Result: Will fast-forward (no merge commit)" -ForegroundColor Green
Write-Host "`n Scenario 2: Three-Way Merge" -ForegroundColor White
Write-Host " Branch: feature-divergent" -ForegroundColor Cyan
Write-Host " Status: BOTH main and branch have new commits" -ForegroundColor Cyan
Write-Host " Result: Will create merge commit" -ForegroundColor Green
Write-Host "`n Scenario 3: Force Merge Commit" -ForegroundColor White
Write-Host " Branch: feature-optional" -ForegroundColor Cyan
Write-Host " Status: Could fast-forward, but we'll use --no-ff" -ForegroundColor Cyan
Write-Host " Result: Will create merge commit even though fast-forward is possible" -ForegroundColor Green
Write-Host "`nNext steps:" -ForegroundColor Cyan
Write-Host " 1. cd challenge" -ForegroundColor White
Write-Host " 2. View initial state: git log --oneline --graph --all" -ForegroundColor White
Write-Host " 3. Merge fast-forward: git merge feature-fast-forward" -ForegroundColor White
Write-Host " 4. View result: git log --oneline --graph" -ForegroundColor White
Write-Host " 5. Merge divergent: git merge feature-divergent" -ForegroundColor White
Write-Host " 6. View result: git log --oneline --graph --all" -ForegroundColor White
Write-Host " 7. Merge with --no-ff: git merge --no-ff feature-optional" -ForegroundColor White
Write-Host " 8. View final result: git log --oneline --graph --all" -ForegroundColor White
Write-Host " 9. Compare all three merges!" -ForegroundColor White
Write-Host " 10. Run '..\verify.ps1' to check your solution" -ForegroundColor White
Write-Host ""

View File

@@ -0,0 +1,140 @@
#!/usr/bin/env pwsh
<#
.SYNOPSIS
Verifies the Module 06 challenge solution.
.DESCRIPTION
This script checks that:
- The challenge directory exists
- A Git repository exists
- All three feature branches have been merged
- Appropriate merge strategies were used
- Student understands the difference between fast-forward and three-way merges
#>
Write-Host "`n=== Verifying Module 06 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 switch main" -ForegroundColor Yellow
$allChecksPassed = $false
}
# Check 1: Fast-Forward Merge - utils.py should exist
if (Test-Path "utils.py") {
Write-Host "[PASS] utils.py exists (feature-fast-forward merged)" -ForegroundColor Green
} else {
Write-Host "[FAIL] utils.py not found" -ForegroundColor Red
Write-Host "[HINT] Merge feature-fast-forward: git merge feature-fast-forward" -ForegroundColor Yellow
$allChecksPassed = $false
}
# Check 2: Three-Way Merge - auth.py should exist
if (Test-Path "auth.py") {
Write-Host "[PASS] auth.py exists (feature-divergent merged)" -ForegroundColor Green
} else {
Write-Host "[FAIL] auth.py not found" -ForegroundColor Red
Write-Host "[HINT] Merge feature-divergent: git merge feature-divergent" -ForegroundColor Yellow
$allChecksPassed = $false
}
# Check 3: --no-ff Merge - config.py should exist
if (Test-Path "config.py") {
Write-Host "[PASS] config.py exists (feature-optional merged)" -ForegroundColor Green
} else {
Write-Host "[FAIL] config.py not found" -ForegroundColor Red
Write-Host "[HINT] Merge feature-optional: git merge --no-ff feature-optional" -ForegroundColor Yellow
$allChecksPassed = $false
}
# Check for merge commits
$mergeCommits = git log --merges --oneline 2>$null
$mergeCount = ($mergeCommits | Measure-Object -Line).Lines
if ($mergeCount -ge 2) {
Write-Host "[PASS] Found $mergeCount merge commit(s)" -ForegroundColor Green
Write-Host "[INFO] Expected: 1 from three-way merge + 1 from --no-ff = 2 total" -ForegroundColor Cyan
} else {
Write-Host "[FAIL] Only found $mergeCount merge commit(s), expected at least 2" -ForegroundColor Red
Write-Host "[HINT] Make sure to:" -ForegroundColor Yellow
Write-Host " 1. Merge feature-divergent (creates merge commit)" -ForegroundColor Yellow
Write-Host " 2. Merge feature-optional with --no-ff flag (forces merge commit)" -ForegroundColor Yellow
$allChecksPassed = $false
}
# Provide detailed merge analysis
Write-Host "`n--- Merge Analysis ---" -ForegroundColor Cyan
# Count total commits
$totalCommits = git rev-list --count HEAD 2>$null
Write-Host "[INFO] Total commits on main: $totalCommits" -ForegroundColor Cyan
# List merge commits
if ($mergeCommits) {
Write-Host "[INFO] Merge commits found:" -ForegroundColor Cyan
$mergeCommits | ForEach-Object {
Write-Host " $_" -ForegroundColor White
}
}
# Check if branches still exist
$branches = git branch 2>$null
Write-Host "[INFO] Existing branches:" -ForegroundColor Cyan
$branches | ForEach-Object {
Write-Host " $_" -ForegroundColor White
}
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 mastered Git merge strategies!" -ForegroundColor Cyan
Write-Host "`nYou now understand:" -ForegroundColor Cyan
Write-Host " - Fast-forward merges (linear history, no merge commit)" -ForegroundColor White
Write-Host " - Three-way merges (divergent branches, creates merge commit)" -ForegroundColor White
Write-Host " - How to force merge commits with --no-ff flag" -ForegroundColor White
Write-Host " - When to use each merge strategy" -ForegroundColor White
Write-Host " - Trade-offs between linear and branched history" -ForegroundColor White
Write-Host "`nKey Takeaways:" -ForegroundColor Yellow
Write-Host " - Git chooses the strategy automatically based on branch state" -ForegroundColor Cyan
Write-Host " - Use --no-ff to preserve feature branch history" -ForegroundColor Cyan
Write-Host " - Use --ff-only to enforce linear history" -ForegroundColor Cyan
Write-Host " - Different workflows prefer different strategies" -ForegroundColor Cyan
Write-Host "`nNow you can make informed decisions about merge strategies for your projects!" -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 ""
Write-Host "Quick Reference:" -ForegroundColor Cyan
Write-Host " git merge feature-fast-forward # Fast-forward merge" -ForegroundColor White
Write-Host " git merge feature-divergent # Three-way merge" -ForegroundColor White
Write-Host " git merge --no-ff feature-optional # Force merge commit" -ForegroundColor White
Write-Host ""
exit 1
}

View File

@@ -1,252 +0,0 @@
# 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 switch -c 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 switch main
git pull origin main
# Create feature branch
git switch -c my-feature
# Do work, make commits
git add .
git commit -m "Add feature"
# Before pushing, update with latest main
git switch main
git pull origin main
git switch 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 switch 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

@@ -1,22 +0,0 @@
#!/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"

View File

@@ -1,147 +0,0 @@
#!/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:
def __init__(self):
self.name = 'TeamProject'
self.version = '1.0.0'
def start(self):
print('Application started')
"@
Set-Content -Path "app.py" -Value $app
git add app.py
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 = @"
from setuptools import setup
setup(
name='team-project',
version='1.0.0',
description='Learning Git remotes',
py_modules=['app'],
)
"@
Set-Content -Path "setup.py" -Value $package
git add setup.py
git commit -m "Add setup.py" | 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.py" -Raw
`$updatedApp = `$appContent -replace "def start\(self\):", @"
def start(self):
print('Starting application...')
self.initialize()
def initialize(self):
"@
Set-Content -Path "app.py" -Value `$updatedApp
git add app.py 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.py (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

@@ -1,174 +0,0 @@
#!/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.py 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.py has been modified
if (-not (Test-Path "app.py")) {
Write-Host "[FAIL] app.py not found." -ForegroundColor Red
Set-Location ../..
exit 1
}
$appContent = Get-Content "app.py" -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.py." -ForegroundColor Red
Write-Host "Hint: Add a new method (like 'stop') to app.py 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

336
README.md
View File

@@ -1,35 +1,43 @@
# Git Workshop # Git Workshop
A comprehensive, hands-on Git workshop with 13 progressive modules covering everything from basic commits to advanced debugging techniques. Each module presents a real-world scenario that you must solve using Git commands, with automated verification to confirm your solution. A comprehensive, hands-on Git workshop with 15 progressive modules covering everything from basic commits to advanced debugging techniques and real-world collaboration. Each module presents a real-world scenario that you must solve using Git commands, with automated verification to confirm your solution.
Perfect for developers who want to move beyond basic Git usage and master professional workflows including rebasing, conflict resolution, bisecting, worktrees, and more. Perfect for developers who want to move beyond basic Git usage and master professional workflows including rebasing, conflict resolution, collaboration, and advanced Git techniques.
## Workshop Structure ## Workshop Structure
Each module is a self-contained challenge that teaches specific git concepts: The workshop is organized into two tracks:
### Beginner Level ### 01 Essentials - Core Git Skills (9 modules)
- **Module 01**: Git Basics - Initialize repositories, stage changes, make commits
- **Module 02**: Viewing History - Use git log and git diff to explore project history
- **Module 03**: Branching Basics - Create, switch, and manage branches
### Intermediate Level Master fundamental Git concepts and collaborative workflows:
- **Module 04**: Merging - Combine branches with fast-forward and three-way merges
- **Module 05**: Merge Conflicts - Identify, understand, and resolve merge conflicts
- **Module 06**: Rebasing - Rebase branches to create linear history
- **Module 07**: Interactive Rebase - Clean up commit history with reset and recommit
- **Module 08**: Cherry-Pick - Apply specific commits from one branch to another
### Advanced Level - **Module 01: Git Basics** - Initialize repositories, stage changes, make commits
- **Module 09**: Reset vs Revert - Understand when to rewrite history vs create new commits - **Module 02: Viewing History** - Use git log and git diff to explore project history
- **Module 10**: Stash - Temporarily save work without committing - **Module 03: Branching Basics** - Create, switch, and manage branches
- **Module 11**: Working with Remotes - Clone, push, pull, and fetch from remote repositories - **Module 04: Merging** - Combine branches and understand merge workflows
- **Module 12**: Worktrees - Work on multiple branches simultaneously - **Module 05: Merge Conflicts** - Identify, understand, and resolve merge conflicts step-by-step
- **Module 13**: Bisect - Use binary search to find bugs in commit history - **Module 06: Cherry-Pick** - Apply specific commits from one branch to another
- **Module 07: Reset vs Revert** - Understand when to rewrite history vs create new commits
- **Module 08: Stash** - Temporarily save work without committing
- **Module 09: Multiplayer Git** - **The Great Print Project** - Real cloud-based collaboration with teammates
### 02 Advanced - Professional Techniques (6 modules)
Advanced Git workflows for power users:
- **Module 01: Rebasing** - Rebase branches to create linear history
- **Module 02: Interactive Rebase** - Clean up commit history before submitting pull requests
- **Module 03: Worktrees** - Work on multiple branches simultaneously
- **Module 04: Bisect** - Use binary search to find bug-introducing commits
- **Module 05: Blame** - Code archaeology - investigate who changed what and when
- **Module 06: Merge Strategies** - Master fast-forward vs three-way merges and when to use each
## How to Use This Workshop ## How to Use This Workshop
1. Navigate to a module directory (e.g., `module-01-basics`) ### For Local Modules (01-08 in Essentials, all Advanced modules)
1. Navigate to a module directory (e.g., `01_essentials/01-basics`)
2. Read the `README.md` to understand the challenge 2. Read the `README.md` to understand the challenge
3. Run `./setup.ps1` to create the challenge environment 3. Run `./setup.ps1` to create the challenge environment
4. Complete the challenge using git commands 4. Complete the challenge using git commands
@@ -38,9 +46,22 @@ Each module is a self-contained challenge that teaches specific git concepts:
**Quick Reference**: See [GIT-CHEATSHEET.md](GIT-CHEATSHEET.md) for a comprehensive list of all Git commands covered in this workshop. Don't worry about memorizing everything - use this as a reference when you need to look up command syntax! **Quick Reference**: See [GIT-CHEATSHEET.md](GIT-CHEATSHEET.md) for a comprehensive list of all Git commands covered in this workshop. Don't worry about memorizing everything - use this as a reference when you need to look up command syntax!
### For Module 09: Multiplayer Git
**This module is different!** It uses a real Git server for authentic collaboration:
1. Navigate to `01_essentials/09-multiplayer`
2. Read the `README.md` for complete instructions
3. **No setup script** - you'll clone from https://git.frod.dk/multiplayer
4. Work with a partner on shared branches
5. Experience real merge conflicts and pull requests
6. **No verify script** - success is visual (your code appears in the final output)
**Facilitators**: See `01_essentials/09-multiplayer/FACILITATOR-SETUP.md` for server setup and workshop guidance.
## Running PowerShell Scripts ## Running PowerShell Scripts
Each module includes setup, verification, and reset scripts. Here's how to run them: Most modules include setup, verification, and reset scripts. Here's how to run them:
### Windows ### Windows
@@ -59,7 +80,8 @@ Then run scripts using:
First, make sure scripts are executable (only needed once): First, make sure scripts are executable (only needed once):
```bash ```bash
chmod +x module-*/setup.ps1 module-*/verify.ps1 module-*/reset.ps1 chmod +x 01_essentials/*/setup.ps1 01_essentials/*/verify.ps1 01_essentials/*/reset.ps1
chmod +x 02_advanced/*/setup.ps1 02_advanced/*/verify.ps1 02_advanced/*/reset.ps1
``` ```
Then run scripts using: Then run scripts using:
@@ -116,6 +138,12 @@ You should see your name and email printed. This is required to make commits in
$PSVersionTable.PSVersion $PSVersionTable.PSVersion
``` ```
### Python (for Module 09 only)
Module 09 (Multiplayer Git) uses Python:
- **Python 3.6+** required to run the Great Print Project
- Check: `python --version` or `python3 --version`
### Basic Command Line Knowledge ### Basic Command Line Knowledge
You should know how to: You should know how to:
@@ -159,16 +187,18 @@ New to Git? Here are the key terms you'll encounter:
- **HEAD**: A pointer showing which commit you're currently working from - **HEAD**: A pointer showing which commit you're currently working from
- **main/master**: The primary branch in a repository (main is the modern convention) - **main/master**: The primary branch in a repository (main is the modern convention)
- **Merge**: Combining changes from different branches - **Merge**: Combining changes from different branches
- **Remote**: A version of your repository hosted elsewhere (like GitHub) - **Remote**: A version of your repository hosted elsewhere (like GitHub, GitLab, or Gitea)
- **Pull Request (PR)**: A request to merge your changes into another branch (used for code review)
- **Conflict**: When Git can't automatically merge changes because both versions modified the same lines
Don't worry if these don't make sense yet - you'll learn them hands-on as you progress! Don't worry if these don't make sense yet - you'll learn them hands-on as you progress!
## Getting Started ## Getting Started
Start with Module 01: Start with Essentials Module 01:
```powershell ```powershell
cd module-01-basics cd 01_essentials/01-basics
.\setup.ps1 .\setup.ps1
``` ```
@@ -177,54 +207,98 @@ Follow the instructions in each module's README.md file.
## What Makes This Workshop Different ## What Makes This Workshop Different
- **Hands-On Practice**: Each module creates a real Git scenario you must solve - **Hands-On Practice**: Each module creates a real Git scenario you must solve
- **Automated Verification**: Scripts check your solution instantly - **Automated Verification**: Scripts check your solution instantly (modules 01-08)
- **Progressive Difficulty**: Builds from basics to advanced Git techniques - **Progressive Difficulty**: Builds from basics to advanced Git techniques
- **Reset Anytime**: Each module includes a reset script for a fresh start - **Reset Anytime**: Each local module includes a reset script for a fresh start
- **Self-Paced**: Learn at your own speed with detailed README guides - **Self-Paced**: Learn at your own speed with detailed README guides
- **No Internet Required**: All modules work offline with local repositories - **Real Collaboration**: Module 09 uses an actual Git server for authentic teamwork
- **Comprehensive Coverage**: From `git init` to advanced rebasing and bisecting
## Learning Path ## Learning Path
The modules are designed to build on each other: The modules are designed to build on each other:
1. **Start Here** (Modules 1-3): Core Git fundamentals everyone needs ### Recommended Progression
2. **Collaboration** (Modules 4-5): Working with branches and resolving conflicts
3. **History Management** (Modules 6-8): Rewriting and organizing commits **Phase 1: Core Fundamentals (Essentials 01-03)**
4. **Advanced Workflows** (Modules 9-11): Professional Git techniques - Git Basics, History, Branching
5. **Power User** (Modules 12-13): Advanced tools for productivity and debugging - **Goal**: Understand commits and branches
**Phase 2: Collaboration Basics (Essentials 04-05)**
- Merging, Merge Conflicts
- **Goal**: Work with multiple branches
**Phase 3: Workflow Tools (Essentials 06-08)**
- Cherry-Pick, Reset vs Revert, Stash
- **Goal**: Manage your work effectively
**Phase 4: Real Collaboration (Essentials 09)**
- **Multiplayer Git - The Great Print Project**
- **Goal**: Apply all skills with real teammates on a cloud server
- **Note**: This is a capstone module - bring everything together!
**Phase 5: Advanced Techniques (Advanced 01-06)**
- Rebasing, Interactive Rebase, Worktrees, Bisect, Blame, Merge Strategies
- **Goal**: Master professional Git workflows
- **When**: After completing Essentials and feeling confident
### Alternative Paths
**Fast Track (1 day workshop):**
- Essentials 01-05 + Essentials 09 (Multiplayer)
**Solo Learner:**
- Complete Essentials 01-08, skip 09 (requires partners and server)
- Or complete all Advanced modules for deep mastery
**Team Workshop:**
- Essentials 01-05 then jump to 09 (Multiplayer) for collaborative practice
## Tips for Success ## Tips for Success
- Don't skip modules - each builds on previous concepts - **Don't skip modules** - each builds on previous concepts
- Read the README.md thoroughly before starting each challenge - **Read the README.md thoroughly** before starting each challenge
- Keep [GIT-CHEATSHEET.md](GIT-CHEATSHEET.md) open as a quick reference - **Keep [GIT-CHEATSHEET.md](GIT-CHEATSHEET.md) open** as a quick reference
- Experiment freely - you can always run `./reset.ps1` to start over - **Experiment freely** - you can always run `./reset.ps1` to start over
- Use `git log --oneline --graph --all` frequently to visualize repository state - **Use `git log --oneline --graph --all`** frequently to visualize repository state
- If stuck, check the Key Concepts section in the module's README - **If stuck**, check the Key Concepts section in the module's README
- Consider installing glow for better markdown reading experience - **Consider installing glow** for better markdown reading experience
- **For Module 09**, work with a partner - collaboration is the point!
## Skills You'll Master ## Skills You'll Master
By completing this workshop, you'll be able to: By completing this workshop, you'll be able to:
### From Essentials Track:
- ✅ Create and manage Git repositories with confidence - ✅ Create and manage Git repositories with confidence
- ✅ Navigate project history and understand what changed when - ✅ Navigate project history and understand what changed when
- ✅ Use branches effectively for parallel development - ✅ Use branches effectively for parallel development
- ✅ Merge branches and resolve conflicts like a pro - ✅ Merge branches and resolve conflicts like a pro
-Rebase to maintain clean, linear history -Apply specific commits with cherry-pick
- ✅ Clean up messy commits before submitting pull requests
- ✅ Cherry-pick specific commits across branches
- ✅ Choose correctly between reset and revert - ✅ Choose correctly between reset and revert
- ✅ Use stash to manage work-in-progress without commits - ✅ Use stash to manage work-in-progress without commits
- ✅ Collaborate effectively with remote repositories -**Collaborate with teammates on shared repositories**
-**Resolve real merge conflicts in a team environment**
-**Create and review pull requests**
-**Use Git on a real cloud server (Gitea)**
### From Advanced Track:
- ✅ Rebase to maintain clean, linear history
- ✅ Clean up messy commits before submitting pull requests
- ✅ Work on multiple branches simultaneously with worktrees - ✅ Work on multiple branches simultaneously with worktrees
- ✅ Debug efficiently by finding bug-introducing commits with bisect - ✅ Debug efficiently by finding bug-introducing commits with bisect
- ✅ Investigate code history with git blame
- ✅ Master different merge strategies and when to use each
These are professional-level Git skills used daily by developers at top tech companies. These are professional-level Git skills used daily by developers at top tech companies.
## For Workshop Facilitators ## For Workshop Facilitators
Before distributing this workshop to attendees: This repository can be used for **self-paced learning** or as a **facilitated workshop**.
### Self-Paced Distribution
Before distributing this workshop to attendees for self-study:
1. **Delete the root `.git` directory**: This prevents confusion and ensures attendees practice git from scratch 1. **Delete the root `.git` directory**: This prevents confusion and ensures attendees practice git from scratch
```powershell ```powershell
@@ -232,3 +306,175 @@ Before distributing this workshop to attendees:
``` ```
2. Each module's `challenge/` directory will become its own independent git repository when attendees run `setup.ps1` 2. Each module's `challenge/` directory will become its own independent git repository when attendees run `setup.ps1`
3. This isolation ensures each module provides a clean learning environment 3. This isolation ensures each module provides a clean learning environment
**Note**: Module 09 (Multiplayer) requires you to set up a Git server - see facilitator guide below.
### Facilitated Workshop
For running this as a full-day instructor-led workshop:
1. **See [WORKSHOP-AGENDA.md](WORKSHOP-AGENDA.md)** - Complete agenda with timing, activities, and facilitation tips
2. **See [PRESENTATION-OUTLINE.md](PRESENTATION-OUTLINE.md)** - Slide deck outline for presentations
3. **Workshop covers:** Essentials 01-05 + Module 09 (Multiplayer collaboration exercise)
4. **Duration:** 6-7 hours including breaks
5. **Format:** Mix of presentation, live demos, and hands-on challenges
**Facilitator preparation:**
- Review the workshop agenda thoroughly
- Set up Git server for Module 09 (see below)
- Ensure all participants have prerequisites installed (Git, PowerShell, Python)
- Prepare slides using the presentation outline
- Test all modules on a clean machine
- Create student accounts on your Git server
The workshop format combines instructor-led sessions with self-paced hands-on modules for an engaging learning experience.
### Setting Up Module 09: Multiplayer Git
Module 09 requires a Git server for authentic collaboration. You have two options:
**Option 1: Self-Hosted Gitea Server (Recommended)**
Run your own Git server with Gitea using Docker and Cloudflare Tunnel:
**Benefits:**
- 💰 Completely free (no cloud costs)
- 🔒 Full control over your data
- 🌐 Accessible from anywhere via Cloudflare Tunnel
- 🚀 Quick setup with Docker Compose
- 👥 Perfect for workshops with 2-24 students
**Setup:**
1. See [GITEA-SETUP.md](GITEA-SETUP.md) for complete Gitea + Docker + Cloudflare Tunnel instructions
2. See `01_essentials/09-multiplayer/FACILITATOR-SETUP.md` for detailed workshop preparation:
- Creating student accounts
- Setting up The Great Print Project repository
- Pairing students
- Monitoring progress
- Troubleshooting common issues
**Option 2: Azure DevOps / GitHub / GitLab**
You can also use existing cloud Git platforms:
- Create organization/group for the workshop
- Set up repository with starter code (see facilitator guide)
- Create user accounts for students
- Configure permissions
**Both options work - Gitea gives you more control and is free for any number of students.**
---
## Repository Structure
```
git-workshop/
├── README.md # This file
├── GIT-CHEATSHEET.md # Quick reference for all Git commands
├── WORKSHOP-AGENDA.md # Facilitator guide for running workshops
├── PRESENTATION-OUTLINE.md # Slide deck outline
├── GITEA-SETUP.md # Self-hosted Git server setup
├── install-glow.ps1 # Install glow markdown renderer
├── 01_essentials/ # Core Git skills (9 modules)
│ ├── 01-basics/ # Initialize, commit, status
│ ├── 02-history/ # Log, diff, show
│ ├── 03-branching/ # Create and switch branches
│ ├── 04-merging/ # Merge branches
│ ├── 05-merge-conflicts/ # Resolve conflicts step-by-step
│ ├── 06-cherry-pick/ # Apply specific commits
│ ├── 07-reset-vs-revert/ # Undo changes safely
│ ├── 08-stash/ # Save work-in-progress
│ └── 09-multiplayer/ # Real collaboration (cloud-based)
│ ├── README.md # Student guide (1,408 lines)
│ └── FACILITATOR-SETUP.md # Server setup guide (904 lines)
└── 02_advanced/ # Professional techniques (6 modules)
├── 01-rebasing/ # Linear history with rebase
├── 02-interactive-rebase/ # Clean up commits
├── 03-worktrees/ # Multiple branches simultaneously
├── 04-bisect/ # Find bugs with binary search
├── 05-blame/ # Code archaeology
└── 06-merge-strategies/ # Master merge techniques
```
## What's Unique About This Workshop
### The Great Print Project (Module 09)
Unlike any other Git tutorial, Module 09 provides **real collaborative experience**:
- **Real Git server**: Not simulated - actual cloud repository at https://git.frod.dk/multiplayer
- **Real teammates**: Work in pairs on shared branches
- **Real conflicts**: Both partners edit the same code and must resolve conflicts together
- **Real pull requests**: Create PRs, review code, merge to main
- **Real success**: When all pairs merge, run `python main.py` and see everyone's contributions!
**The challenge**: Each pair implements 3 Python functions (e.g., `print_b()`, `print_c()`, `print_d()`) in a shared repository. When complete, the program prints the alphabet A-Z and numbers 0-9.
**What students learn**:
- Push/pull workflow with real teammates
- Handling rejected pushes (partner pushed first!)
- Resolving actual merge conflicts (not simulated)
- Creating meaningful pull requests
- Reviewing others' code
- Staying synchronized with a team
This is how professional developers actually work - no simulation, no shortcuts.
---
## Frequently Asked Questions
**Q: Do I need to complete all modules?**
A: No! Essentials 01-05 covers what most developers use daily. Complete 06-09 and Advanced modules to deepen your skills.
**Q: Can I do Module 09 (Multiplayer) without a partner?**
A: Not recommended - collaboration is the point. If solo, skip to Advanced modules or wait until you can pair with someone.
**Q: How long does the workshop take?**
A:
- Essentials 01-05: 3-4 hours
- Full Essentials (01-09): 6-7 hours
- All modules: 12-15 hours
- Self-paced over several days works great!
**Q: I'm stuck on a module. What should I do?**
A:
1. Re-read the module README.md
2. Check [GIT-CHEATSHEET.md](GIT-CHEATSHEET.md)
3. Run `./reset.ps1` to start fresh
4. Use `git status` and `git log --graph` to understand current state
5. For Module 09, ask your partner or facilitator
**Q: Can I use this for a team workshop at my company?**
A: Absolutely! See the "For Workshop Facilitators" section above. The materials are designed for both self-study and instructor-led workshops.
**Q: Do I need internet access?**
A: Modules 01-08 work completely offline. Module 09 requires internet to access the Git server.
**Q: What if I prefer GitHub/GitLab instead of Gitea?**
A: The skills are identical across all Git platforms. Module 09 uses Gitea but everything you learn applies to GitHub, GitLab, Bitbucket, etc.
---
## Contributing
Found a bug or have a suggestion? Please open an issue or submit a pull request!
---
## License
This workshop is provided as-is for educational purposes.
---
**Ready to master Git? Start with Essentials Module 01 and begin your journey!**
```powershell
cd 01_essentials/01-basics
.\setup.ps1
```
Happy Learning! 🚀