Compare commits

..

18 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
Bjarke Sporring
6b0e84934a feat: split out git blame 2026-01-07 21:12:31 +01:00
Bjarke Sporring
30b878fc67 feat: add git blame section 2026-01-07 20:22:53 +01:00
Bjarke Sporring
aefcfbe100 feat: add more challenges to the history module 2026-01-07 20:12:54 +01:00
Bjarke Sporring
5f8091d5b2 feat: add advanced node 2026-01-07 19:26:11 +01:00
Bjarke Sporring
cf073d569e refactor: move modules into levels 2026-01-07 17:59:02 +01:00
Bjarke Sporring
d7c146975d feat: add understanding for diffs 2026-01-05 13:34:59 +01:00
Bjarke Sporring
dc94520b2a feat: add guidance for beginners to get started 2026-01-05 12:47:33 +01:00
Bjarke Sporring
e29b9bca70 feat: use switch instead of checkout 2026-01-05 12:38:35 +01:00
Bjarke Sporring
a7b511c8cb feat: add git cheatsheet 2026-01-05 12:34:40 +01:00
Bjarke Sporring
a8eb66d3c9 feat: update readme 2026-01-05 12:22:01 +01:00
69 changed files with 6248 additions and 1255 deletions

View File

@@ -53,20 +53,53 @@ git status # Show the working tree status
git add <file> # Stage a specific file for commit
git add . # Stage all files in current directory
git commit -m "<message>" # Create a commit with a message
git ls-tree -r HEAD --name-only # List all files in the latest commit
git ls-tree -r HEAD --name-only # ADVANCED: List all files in the latest commit
git log # View commit history
```
### Verification
Once you think you've completed the challenge, run:
Once you think you've completed the challenge, run the verification script.
**Important:** Run this from the **module directory**, not the challenge directory.
```powershell
# If you're in the challenge directory, go back up:
cd ..
# Then verify:
.\verify.ps1
```
This will check if you've successfully completed all the steps.
### Troubleshooting
**Error: "fatal: unable to auto-detect email address"**
This means Git doesn't know who you are yet. You need to configure your name and email:
```powershell
git config user.name "Your Name"
git config user.email "your.email@example.com"
```
Then try your commit again. For more details, see the "Requirements" section in the main README.md.
**Error: "Not a git repository"**
Make sure you ran `git init` in the challenge directory. This creates a hidden `.git` folder that tracks your project.
**Can't find the challenge directory?**
Make sure you ran `.\setup.ps1` first from the module directory. This creates the `challenge/` folder.
**Where am I?**
Use `pwd` (Print Working Directory) to see your current location:
- If you're in something like `.../module-01-basics/challenge`, you're in the challenge directory
- If you're in something like `.../module-01-basics`, you're in the module directory
### Need to Start Over?
If you want to reset the challenge and start fresh, run:

View File

@@ -0,0 +1,225 @@
# Module 02: Viewing History
## Learning Objectives
In this module, you will:
- Understand commit history and how to navigate it
- Use `git log` to view commit history with various formats
- Use `git show` to view specific commit details
- Use `git diff` to compare changes between commits
- Use `git diff --staged` to view changes ready to be committed
- Understand commit hashes and references
- Discover how `git diff` reveals changes not visible in current files
## Challenge
### Setup
Run the setup script to create your challenge environment:
```powershell
.\setup.ps1
```
This will create a `challenge/` directory with a Git repository that already has some commit history.
### Your Task
You'll explore an existing Git repository that contains multiple commits. Your goal is to use Git commands to discover information about the repository's history, including:
- Viewing commit history
- Examining changes between specific commits
- Understanding staged changes
- **Finding a secret code hidden in the commit history!** (Only discoverable by using `git diff`)
The setup script will create an `answers.md` file in the challenge directory with questions for you to answer. Fill in your answers directly in that file.
**Suggested Approach:**
1. Navigate to the challenge directory: `cd challenge`
2. Open `answers.md` to see the questions
3. View the commit history: `git log` or `git log --oneline`
4. Check repository status: `git status`
5. View staged changes: `git diff --staged`
6. Try different log formats: `git log --stat`, `git log --graph`
7. View specific commits: `git show <commit-hash>`
8. Compare specific commits: `git diff <commit1> <commit2> <file>`
9. Fill in your answers in `answers.md`
> **Important Notes:**
> - You can use any Git commands you like to explore the repository
> - Fill in your answers directly in the `answers.md` file (there are placeholder sections for each answer)
> - Commit hashes can be referenced by their full hash or just the first 7 characters
> - Notice that one file is already staged - use `git diff --staged` to see what it contains
## Key Concepts
- **Commit Hash**: A unique identifier (SHA-1 hash) for each commit. You can use the full hash or just the first few characters.
- **Commit Message**: A description of what changed in that commit, written by the author.
- **Commit History**: The chronological record of all changes made to a repository.
- **HEAD**: A pointer to the current commit you're working from.
- **Diff**: A view showing the differences between two versions of files.
- **Staging Area**: Where changes wait before being committed. Use `git diff --staged` to see what's ready to commit.
- **Working Directory vs Staged vs Committed**:
- Working Directory: Files you're currently editing
- Staged (Index): Changes marked for the next commit (via `git add`)
- Committed: Changes permanently saved in history
## Understanding Diff Output
When you run `git diff` between commits, the output can look confusing at first. Here's how to read it:
### Example Diff Output
```diff
diff --git a/app.py b/app.py
index 1a2b3c4..5d6e7f8 100644
--- a/app.py
+++ b/app.py
@@ -1,5 +1,7 @@
# app.py - Main application file
+from auth import login, logout
def main():
print("Welcome to My App!")
- # Application initialization code here
+ login("user", "password")
pass
```
### Breaking It Down
**1. File Header**
```diff
diff --git a/app.py b/app.py
```
- Shows which file is being compared
- `a/app.py` = old version (before)
- `b/app.py` = new version (after)
**2. Metadata**
```diff
index 1a2b3c4..5d6e7f8 100644
--- a/app.py
+++ b/app.py
```
- `---` indicates the old version
- `+++` indicates the new version
- The hashes (1a2b3c4, 5d6e7f8) are internal Git identifiers
**3. Change Location (Hunk Header)**
```diff
@@ -1,5 +1,7 @@
```
- `@@ -1,5 +1,7 @@` tells you where changes occurred
- `-1,5` = in the old file, starting at line 1, showing 5 lines
- `+1,7` = in the new file, starting at line 1, showing 7 lines
- The file grew by 2 lines (from 5 to 7)
**4. The Actual Changes**
Lines are prefixed with symbols:
- ` ` (space) = unchanged line (context)
- `-` (minus) = line removed from old version (shown in red in terminal)
- `+` (plus) = line added in new version (shown in green in terminal)
In our example:
```diff
# app.py - Main application file ← unchanged
+from auth import login, logout ← added (new)
def main(): ← unchanged
print("Welcome to My App!") ← unchanged
- # Application initialization code here ← removed (old)
+ login("user", "password") ← added (new)
pass ← unchanged
```
### Reading Multiple Files
If multiple files changed, you'll see multiple diff sections:
```diff
diff --git a/app.py b/app.py
[changes to app.py]
diff --git a/auth.py b/auth.py
[changes to auth.py]
```
### Pro Tips
- **Context lines**: Unchanged lines around changes help you understand where the change happened
- **Color coding**: In your terminal, deletions are usually red, additions are green
- **No newline warning**: If you see `\ No newline at end of file`, it means the file doesn't end with a newline character (usually not important for beginners)
- **Binary files**: For images or other binary files, Git just says "Binary files differ"
### Try It Yourself
In this module's challenge, you'll use:
```bash
# See what's staged for the next commit
git diff --staged
# Compare changes between specific commits
git diff <commit2> <commit4> app.py
```
Pay attention to:
- Which lines were added (green, with `+`)
- Which lines were removed (red, with `-`)
- The surrounding context (white, with space)
- How `git diff --staged` shows only changes ready to commit
## Useful Commands
### Viewing History
```bash
git log # View commit history
git log --oneline # Compact one-line format
git log --stat # Show files changed in each commit
git log --graph # Show branch graph (more useful with branches)
git show <commit> # View specific commit details
git show <commit>:<file> # View a file from a specific commit
```
### Comparing Changes with git diff
```bash
# Compare commits
git diff <commit1> <commit2> # Compare two commits
git diff <commit1> <commit2> <file> # Compare specific file between commits
git diff <commit> # Compare commit with current working directory
# Compare staged changes
git diff --staged # Show changes in staging area (ready to commit)
git diff --cached # Same as --staged (alternative syntax)
git diff # Show unstaged changes in working directory
git diff HEAD # Show all changes (staged + unstaged) vs last commit
```
**When to use each `git diff` variant:**
- `git diff` - See what you've changed but haven't staged yet
- `git diff --staged` - Review what you're about to commit
- `git diff HEAD` - See all your changes since the last commit
- `git diff <commit1> <commit2>` - Compare any two points in history
## Verification
Once you've filled in your answers in `answers.md`, verify your solution:
```powershell
.\verify.ps1
```
The verification script will check that your answers contain the expected information.
## Need to Start Over?
If you want to reset the challenge and start fresh:
```powershell
.\reset.ps1
```
This will remove the challenge directory and run the setup script again, giving you a clean slate.

View File

@@ -87,6 +87,7 @@ git commit -m "Add user authentication" | Out-Null
Write-Host "Adding database connection..." -ForegroundColor Green
$databaseContent = @"
# database.py - Database connection module
# SECRET_CODE: UNICORN
def connect():
# Connect to database
@@ -140,6 +141,22 @@ def logout(username):
"@
Set-Content -Path "auth.py" -Value $authContent
# Remove the secret code from database.py
$databaseContent = @"
# database.py - Database connection module
def connect():
# Connect to database
print("Connecting to database...")
return True
def disconnect():
# Disconnect from database
print("Disconnecting from database...")
return True
"@
Set-Content -Path "database.py" -Value $databaseContent
git add .
git commit -m "Fix authentication bug" | Out-Null
@@ -183,52 +200,155 @@ Set-Content -Path "app.py" -Value $appContent
git add .
git commit -m "Add user profile feature" | Out-Null
# Create a staged change scenario
Write-Host "Creating staged changes for exploration..." -ForegroundColor Green
$configContent = @"
# config.py - Configuration settings
DEBUG_MODE = False
DATABASE_URL = "sqlite:///app.db"
"@
Set-Content -Path "config.py" -Value $configContent
git add config.py
# Create answers.md template
Write-Host "Creating answers.md template..." -ForegroundColor Green
$answersTemplate = @"
# Git History Exploration - Answers
Answer the following questions by exploring the Git repository history.
## Question 1: How many commits are in the repository?
**Your Answer:**
<!-- Write your answer here -->
## Question 2: What was the commit message for the third commit?
(Counting from the first/oldest commit)
**Your Answer:**
<!-- Write your answer here -->
## Question 3: Which file was modified in the "Fix authentication bug" commit?
**Your Answer:**
<!-- Write your answer here -->
## Question 4: What changes were made to app.py between the first and last commits?
Briefly describe the main changes you observe.
**Your Answer:**
<!-- Write your answer here -->
Welcome! Answer the following questions by exploring the Git repository history using Git commands.
**Instructions:**
- Replace the "Write your answer here" comments with your actual answers
- You can use any Git commands to explore the repository
- The hints section at the bottom provides helpful commands
---
**Hints:**
- Use `git log` or `git log --oneline` to view commit history
- Use `git log --stat` to see which files were changed in each commit
- Use `git show <commit-hash>` to view details of a specific commit
- Use `git diff <commit1> <commit2> <file>` to compare changes between commits
## Question 1: How many commits are in the repository?
**Suggested commands:**
``````bash
git log --oneline
# Or count them with:
git rev-list --count HEAD
``````
**Your Answer:**
<!-- Write your answer here -->
---
## Question 2: What was the commit message for the third commit?
**Note:** Count from the first/oldest commit (the one at the bottom of `git log`)
**Suggested commands:**
``````bash
git log --oneline --reverse # Shows oldest first
git log # Shows newest first
``````
**Your Answer:**
<!-- Write your answer here -->
---
## Question 3: Which files were modified in the "Fix authentication bug" commit?
**Note:** There are multiple files - list all of them!
**Suggested commands:**
``````bash
git log --stat # Shows files changed in each commit
git show <commit-hash> --name-only # Shows only filenames for a commit
``````
**Your Answer:**
<!-- Write your answer here (list all files) -->
---
## Question 4: What new file is currently staged (ready to be committed)?
**Suggested commands:**
``````bash
git status # Shows staged, unstaged, and untracked files
git diff --staged # Shows content of staged changes
``````
**Your Answer:**
<!-- Write your answer here -->
---
## Question 5: Find the secret code hidden in the commit history! 🔍
**The Challenge:**
A secret code was added in one commit and then removed in a later commit. It doesn't exist in the current files - you can ONLY find it by comparing commits with `git diff`.
**Hint:** The secret code is in `database.py` and exists in the third commit but was removed in the fourth commit.
**Suggested commands:**
``````bash
# First, get the commit hashes
git log --oneline
# Then compare the third and fourth commits
git diff <commit3-hash> <commit4-hash> database.py
# Look for lines that were removed (marked with - in red)
``````
**Your Answer (what is the secret code?):**
<!-- Write your answer here -->
---
---
## Quick Reference - Useful Commands
**Viewing History:**
``````bash
git log # View commit history (newest first)
git log --oneline # Compact one-line format
git log --reverse # Show oldest commits first
git log --stat # Show files changed in each commit
git log --graph --all # Visual branch graph
``````
**Viewing Specific Commits:**
``````bash
git show <commit-hash> # View commit details
git show <commit-hash> --stat # Show files changed
git show <commit-hash>:file.txt # View file from specific commit
``````
**Comparing Changes:**
``````bash
git diff <commit1> <commit2> # Compare two commits
git diff <commit1> <commit2> file # Compare specific file
git diff --staged # Show staged changes
git diff # Show unstaged changes
git diff HEAD # Show all changes since last commit
``````
**Repository Status:**
``````bash
git status # Show working tree status
git ls-files # List all tracked files
``````
**Pro Tip:** You can use just the first 7 characters of a commit hash (e.g., `a1b2c3d` instead of the full hash)
---
When you're done, run ``..\verify.ps1`` to check your answers!
"@
Set-Content -Path "answers.md" -Value $answersTemplate

View File

@@ -59,23 +59,40 @@ if (-not (Test-Path "answers.md")) {
$allChecksPassed = $false
}
# Check 3: Contains "auth" keyword for file modified in bug fix
if ($answersLower -match "auth") {
Write-Host "[PASS] Correct file identified for bug fix commit" -ForegroundColor Green
# Check 3: Contains both "auth" and "database" keywords for files modified in bug fix
$hasAuth = $answersLower -match "auth"
$hasDatabase = $answersLower -match "database"
if ($hasAuth -and $hasDatabase) {
Write-Host "[PASS] Both files identified for bug fix commit" -ForegroundColor Green
} elseif ($hasAuth -or $hasDatabase) {
Write-Host "[PARTIAL] Only one file found - there are TWO files modified in this commit" -ForegroundColor Yellow
Write-Host "[HINT] Use 'git log --stat' or 'git show <commit-hash> --name-only' to see ALL files changed" -ForegroundColor Yellow
$allChecksPassed = $false
} else {
Write-Host "[FAIL] File modified in bug fix commit not found" -ForegroundColor Red
Write-Host "[FAIL] Files modified in bug fix commit not found" -ForegroundColor Red
Write-Host "[HINT] Use 'git log --stat' to see which files were changed in each commit" -ForegroundColor Yellow
$allChecksPassed = $false
}
# Check 4: Some mention of changes (flexible check)
if ($answersLower -match "import|profile|function|added|login|connect") {
Write-Host "[PASS] Changes to app.py described" -ForegroundColor Green
# Check 4: Contains "config" keyword for staged file
if ($answersLower -match "config") {
Write-Host "[PASS] Staged file identified" -ForegroundColor Green
} else {
Write-Host "[FAIL] Changes to app.py not described" -ForegroundColor Red
Write-Host "[HINT] Use 'git diff <first-commit> <last-commit> app.py' to see changes" -ForegroundColor Yellow
Write-Host "[FAIL] Staged file not identified" -ForegroundColor Red
Write-Host "[HINT] Use 'git status' or 'git diff --staged' to see staged changes" -ForegroundColor Yellow
$allChecksPassed = $false
}
# Check 5: Contains "unicorn" for the secret code
if ($answersLower -match "unicorn") {
Write-Host "[PASS] Secret code found!" -ForegroundColor Green
} else {
Write-Host "[FAIL] Secret code not found" -ForegroundColor Red
Write-Host "[HINT] Use 'git diff <commit3> <commit4> database.py' to find the secret code" -ForegroundColor Yellow
$allChecksPassed = $false
}
}
Set-Location ..
@@ -91,7 +108,8 @@ if ($allChecksPassed) {
Write-Host " - View commit history with git log" -ForegroundColor White
Write-Host " - Find specific commits and their messages" -ForegroundColor White
Write-Host " - See which files changed in commits" -ForegroundColor White
Write-Host " - Compare changes between commits with git diff" -ForegroundColor White
Write-Host " - Check staged changes with git diff --staged" -ForegroundColor White
Write-Host " - Compare changes between specific commits with git diff" -ForegroundColor White
Write-Host "`nReady for the next module!" -ForegroundColor Green
Write-Host ""
} else {

View File

@@ -4,8 +4,8 @@
In this module, you will:
- Understand what a branch is in Git
- Create new branches using `git branch` or `git checkout -b`
- Switch between branches using `git checkout` or `git switch`
- Create new branches using `git branch` or `git switch -c`
- Switch between branches using `git switch`
- View all branches with `git branch`
- Understand that branches allow parallel development
@@ -38,19 +38,18 @@ Your goal is to create a feature branch, make commits on it, and understand how
1. Navigate to the challenge directory: `cd challenge`
2. View existing branches: `git branch`
3. Create and switch to new branch: `git checkout -b feature-login`
3. Create and switch to new branch: `git switch -c feature-login`
4. Create `login.py` with any content you like
5. Stage and commit: `git add login.py` and `git commit -m "Add login functionality"`
6. Modify `login.py`, then commit again
7. Switch back to main: `git checkout main`
8. Run `ls` and notice that `login.py` doesn't exist on main!
9. Switch back to feature-login: `git checkout feature-login`
10. Run `ls` again and see that `login.py` is back!
7. Switch back to main: `git switch 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`
10. Run `ls` (or check your file explorer) again and see that `login.py` is back!
> **Important Notes:**
> - You can use either `git checkout` or `git switch` to change branches
> - `git checkout -b <name>` creates and switches in one command
> - `git switch -c <name>` is the newer equivalent
> - Use `git switch` to change branches
> - `git switch -c <name>` creates and switches in one command
> - 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
@@ -66,10 +65,9 @@ Your goal is to create a feature branch, make commits on it, and understand how
```bash
git branch # List all branches (* shows current)
git branch <name> # Create a new branch
git checkout <branch> # Switch to an existing branch
git checkout -b <name> # Create and switch to new branch
git switch <branch> # Switch to a branch (newer syntax)
git switch -c <name> # Create and switch (newer syntax)
git switch <branch> # Switch to an existing branch
git switch -c <name> # Create and switch to new branch
git switch - # Switch back to previous branch
git branch -d <name> # Delete a branch (we won't use this yet)
```

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 "`nNext steps:" -ForegroundColor Cyan
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 " 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 " 7. Run '..\verify.ps1' to check your solution" -ForegroundColor White
Write-Host ""

View File

@@ -40,7 +40,7 @@ if ($branchExists) {
Write-Host "[PASS] Branch 'feature-login' exists" -ForegroundColor Green
} else {
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
Set-Location ..
exit 1
@@ -67,7 +67,7 @@ if (Test-Path "login.py") {
}
# 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")) {
Write-Host "[PASS] File 'login.py' does NOT exist in main branch (branches are independent!)" -ForegroundColor Green
} else {
@@ -78,7 +78,7 @@ if (-not (Test-Path "login.py")) {
# Switch back to original branch
if ($originalBranch) {
git checkout $originalBranch 2>$null | Out-Null
git switch $originalBranch 2>$null | Out-Null
}
Set-Location ..
@@ -91,7 +91,7 @@ if ($allChecksPassed) {
Write-Host "=====================================" -ForegroundColor Green
Write-Host "`nYou've successfully learned about Git branches!" -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 " - That branches are independent lines of development" -ForegroundColor White
Write-Host " - That files in one branch don't affect another" -ForegroundColor White

View File

@@ -0,0 +1,229 @@
# Module 04: Merging Branches
## Learning Objectives
In this module, you will:
- Understand what merging means in Git
- Merge a feature branch back into main
- Use `git merge` to combine branches
- Visualize merged branches with `git log --graph`
## Challenge
### Setup
Run the setup script to create your challenge environment:
```powershell
.\setup.ps1
```
This will create a `challenge/` directory with a Git repository that has a main branch and a feature branch.
### Your Task
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!
**Scenario:**
- You created a `feature-login` branch to add login functionality
- 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
**Steps:**
1. Navigate to the challenge directory: `cd challenge`
2. Check which branch you're on: `git branch`
3. View all branches: `git log --oneline --graph --all`
4. Merge the feature-login branch into main: `git merge feature-login`
5. View the result: `git log --oneline --graph --all`
> **That's it!** Merging is how you bring work from one branch into another.
## What is Merging?
**Merging** is the process of taking changes from one branch and bringing them into another branch.
Think of it like combining two streams into one river - all the water (code) flows together.
### 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
```bash
# Merging
git merge <branch-name> # Merge a branch into current branch
# Viewing
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
Once you've merged the feature-login branch, verify your solution:
```powershell
.\verify.ps1
```
The verification script will check that you've successfully merged.
## Need to Start Over?
If you want to reset the challenge and start fresh:
```powershell
.\reset.ps1
```
## 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

@@ -0,0 +1,246 @@
#!/usr/bin/env pwsh
<#
.SYNOPSIS
Sets up the Module 04 challenge environment for learning about merging.
.DESCRIPTION
This script creates a challenge directory with a Git repository that
contains two divergent branches ready to merge (three-way merge scenario).
#>
Write-Host "`n=== Setting up Module 04 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"
# Commit 1: Initial project structure on main
Write-Host "Creating initial project structure..." -ForegroundColor Green
$readmeContent = @"
# My Project
A simple web application project.
## Setup
Coming soon...
"@
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 commit -m "Initial project structure" | Out-Null
# Commit 2: Add configuration file on main
Write-Host "Adding configuration..." -ForegroundColor Green
$configContent = @"
# config.py - Configuration settings
APP_NAME = "My Project"
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
def disconnect():
"""Disconnect from database."""
print("Disconnecting...")
return True
"@
Set-Content -Path "database.py" -Value $dbContent
git add .
git commit -m "Add database utilities" | Out-Null
# Create feature-login branch
Write-Host "Creating feature-login branch..." -ForegroundColor Green
git switch -c feature-login | Out-Null
# Commit 4: Add login module on feature-login
Write-Host "Adding login functionality on feature-login..." -ForegroundColor Green
$loginContent = @"
# login.py - User login module
def login(username, password):
"""Authenticate a user."""
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 "login.py" -Value $loginContent
git add .
git commit -m "Add login module" | Out-Null
# Commit 5: Add password validation on feature-login
$loginContent = @"
# login.py - User login module
def validate_password(password):
"""Validate password strength."""
if len(password) < 8:
return False
return True
def login(username, password):
"""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 "login.py" -Value $loginContent
git add .
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
Write-Host "Switching back to main branch..." -ForegroundColor Green
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
Set-Location ..
Write-Host "`n=== Setup Complete! ===" -ForegroundColor Green
Write-Host "`nYour challenge environment is ready in the 'challenge/' directory." -ForegroundColor Cyan
Write-Host "`nScenario: You have two divergent branches!" -ForegroundColor Yellow
Write-Host " - main: Has 5 commits (config, database utils, README updates)" -ForegroundColor White
Write-Host " - feature-login: Has 4 commits (login, validation, session)" -ForegroundColor White
Write-Host "`nNext steps:" -ForegroundColor Cyan
Write-Host " 1. cd challenge" -ForegroundColor White
Write-Host " 2. View the branch structure: git log --oneline --graph --all" -ForegroundColor White
Write-Host " 3. Merge feature-login into main: git merge feature-login" -ForegroundColor White
Write-Host " 4. View the merge result: git log --oneline --graph --all" -ForegroundColor White
Write-Host " 5. Run '..\verify.ps1' to check your solution" -ForegroundColor White
Write-Host ""

View File

@@ -0,0 +1,122 @@
#!/usr/bin/env pwsh
<#
.SYNOPSIS
Verifies the Module 04 challenge solution.
.DESCRIPTION
This script checks that:
- The challenge directory exists
- A Git repository exists
- Currently on main branch
- feature-login has been merged into main
- A merge commit exists (three-way merge)
- Login functionality is present on main
#>
Write-Host "`n=== Verifying Module 04 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 if feature-login branch exists
$featureLoginBranch = git branch --list "feature-login" 2>$null
if ($featureLoginBranch) {
Write-Host "[PASS] Branch 'feature-login' exists" -ForegroundColor Green
} else {
Write-Host "[INFO] Branch 'feature-login' not found (may have been deleted after merge)" -ForegroundColor Cyan
}
# Check if login.py exists (should be on main after merge)
if (Test-Path "login.py") {
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
}
# Check if app.py contains login integration
if (Test-Path "app.py") {
$appContent = Get-Content "app.py" -Raw
if ($appContent -match "login") {
Write-Host "[PASS] app.py contains login integration" -ForegroundColor Green
} else {
Write-Host "[FAIL] app.py doesn't contain login integration" -ForegroundColor Red
Write-Host "[HINT] After merging, app.py should import and use the login module" -ForegroundColor Yellow
$allChecksPassed = $false
}
} else {
Write-Host "[FAIL] app.py not found" -ForegroundColor Red
$allChecksPassed = $false
}
# Check for merge commit (indicates three-way merge happened)
$mergeCommits = git log --merges --oneline 2>$null
if ($mergeCommits) {
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 {
Write-Host "[FAIL] No merge commit found" -ForegroundColor Red
Write-Host "[HINT] Merge feature-login into main with: git merge feature-login" -ForegroundColor Yellow
$allChecksPassed = $false
}
# Check if both branches contributed commits (true three-way merge)
$totalCommits = git rev-list --count HEAD 2>$null
if ($totalCommits -ge 4) {
Write-Host "[PASS] Repository has $totalCommits commits (branches diverged properly)" -ForegroundColor Green
} else {
Write-Host "[WARN] Repository has only $totalCommits commits (expected at least 4)" -ForegroundColor Yellow
Write-Host "[INFO] This might still be correct if you deleted commits" -ForegroundColor Cyan
}
Set-Location ..
# Final summary
if ($allChecksPassed) {
Write-Host "`n" -NoNewline
Write-Host "=====================================" -ForegroundColor Green
Write-Host " CONGRATULATIONS! CHALLENGE PASSED!" -ForegroundColor Green
Write-Host "=====================================" -ForegroundColor Green
Write-Host "`nYou've successfully completed your first branch merge!" -ForegroundColor Cyan
Write-Host "You now understand:" -ForegroundColor Cyan
Write-Host " - How to merge branches with git merge" -ForegroundColor White
Write-Host " - What a merge commit is and why it's created" -ForegroundColor White
Write-Host " - How divergent branches are combined" -ForegroundColor White
Write-Host " - How to visualize merges with git log --graph" -ForegroundColor White
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 ""
} 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 ""
exit 1
}

View File

@@ -0,0 +1,481 @@
# Module 05: Merge Conflicts
## Learning Objectives
By the end of this module, you will:
- Understand what merge conflicts are and why they occur
- Identify merge conflicts in your repository
- Read and interpret conflict markers
- Resolve merge conflicts manually step-by-step
- Complete a merge after resolving conflicts
## 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.
**Your mission:**
1. Attempt to merge the `update-config` branch into `main`
2. Git will tell you there's a conflict - don't panic!
3. Resolve the conflict by keeping BOTH settings (timeout AND debug)
4. Complete the merge
**Steps to follow:**
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
## What Are Merge Conflicts?
A **merge conflict** occurs when Git cannot automatically combine changes because both branches modified the same lines in the same file.
**Example scenario:**
```
main branch changes line 5: TIMEOUT = 30
feature branch changes line 5: DEBUG = True
```
Git doesn't know which one you want! So it asks you to decide.
**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!)
## Step-by-Step: Resolving Your First Conflict
### Step 1: Attempt the Merge
```bash
cd challenge
git merge update-config
```
**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
Once you've resolved the conflict and completed the merge, verify your solution:
```powershell
.\verify.ps1
```
The verification will check that:
- ✅ The merge conflict was resolved
- ✅ The merge was completed successfully
- ✅ BOTH settings are in the config file (TIMEOUT and DEBUG)
- ✅ NO conflict markers remain
- ✅ The merge commit exists in history
---
## Need to Start Over?
If you want to reset the challenge and start fresh:
```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
# Switch to feature branch: Add debug setting (conflicting change)
git checkout update-config | Out-Null
git switch update-config | Out-Null
$featureConfig = @"
{
@@ -78,7 +78,7 @@ git add config.json
git commit -m "Add debug mode configuration" | Out-Null
# Switch back to main branch
git checkout main | Out-Null
git switch main | Out-Null
# Return to module directory
Set-Location ..

View File

@@ -128,7 +128,8 @@ if ($parentCount -ne 2) {
}
# 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") {
Write-Host "[FAIL] The 'update-config' branch was not merged." -ForegroundColor Red
Set-Location ..

View File

@@ -112,7 +112,7 @@ The verification will check that:
2. You're currently on the development branch
3. View the commits: `git log --oneline`
4. You'll see several commits - identify the bug fixes
5. Switch to main branch: `git checkout main`
5. Switch to main branch: `git switch main`
6. Cherry-pick the bug fix commits (you'll need their commit hashes)
7. Verify the result with `git log --oneline`
8. Run the verification script
@@ -131,7 +131,7 @@ The verification will check that:
### Hotfix to Production
You have a critical bug fix on a development branch that needs to go to production immediately:
```bash
git checkout production
git switch production
git cherry-pick <bugfix-commit-hash>
```
@@ -141,17 +141,17 @@ You accidentally committed on the wrong branch:
# On wrong branch, note the commit hash
git log --oneline
# Switch to correct branch
git checkout correct-branch
git switch correct-branch
git cherry-pick <commit-hash>
# Go back and remove from wrong branch
git checkout wrong-branch
git switch wrong-branch
git reset --hard HEAD~1
```
### Backporting
You need to apply a fix to an older release branch:
```bash
git checkout release-2.0
git switch release-2.0
git cherry-pick <fix-from-main>
```

View File

@@ -135,7 +135,7 @@ The verification will check that:
2. You're on the local-feature branch with a bad commit
3. View commits: `git log --oneline`
4. Use `git reset --hard HEAD~1` to remove the bad commit
5. Switch to shared-feature: `git checkout shared-feature`
5. Switch to shared-feature: `git switch shared-feature`
6. View commits: `git log --oneline`
7. Find the hash of the "Add broken feature" commit
8. Use `git revert <commit-hash>` to undo it safely

View File

@@ -121,10 +121,10 @@ The verification will check that:
3. Check status: `git status` (you'll see modified files)
4. Stash your changes: `git stash save "WIP: login feature"`
5. Verify working directory is clean: `git status`
6. Switch to main: `git checkout main`
6. Switch to main: `git switch main`
7. View the bug in app.js and fix it (remove the incorrect line)
8. Commit the fix: `git add app.js && git commit -m "Fix critical security bug"`
9. Switch back to feature: `git checkout feature-login`
9. Switch back to feature: `git switch feature-login`
10. Restore your work: `git stash pop`
11. Complete the feature (the TODOs in login.js)
12. Commit your completed feature
@@ -147,9 +147,9 @@ The verification will check that:
```bash
# Working on feature, need to switch to main
git stash
git checkout main
git switch main
# Do work on main
git checkout feature
git switch feature
git stash pop
```
@@ -176,10 +176,10 @@ git stash pop # Restore original work
```bash
# Same fix needed on multiple branches
git stash
git checkout branch1
git switch branch1
git stash apply
git commit -am "Apply fix"
git checkout branch2
git switch branch2
git stash apply
git commit -am "Apply fix"
git stash drop # Clean up when done

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

@@ -86,7 +86,7 @@ git rebase --abort
git branch
# Switch to a branch
git checkout <branch-name>
git switch <branch-name>
```
## Verification

View File

@@ -0,0 +1,169 @@
# Module 05: Git Blame - Code Archaeology
## Learning Objectives
In this module, you will:
- Use `git blame` to find who made specific changes
- Understand blame output format and information
- Track down problematic code changes
- Learn when and why to use `git blame`
- Investigate code history to understand context
## Challenge
### Setup
Run the setup script to create your challenge environment:
```powershell
.\setup.ps1
```
This will create a `challenge/` directory with a Git repository that has a security issue - someone committed hardcoded credentials!
### Your Task
Your team has discovered a security vulnerability: hardcoded credentials were added to the codebase. Your job is to investigate who made this change and document your findings.
The setup script will create an `investigation.md` file in the challenge directory with questions for you to answer. Use `git blame` and other Git commands to track down the responsible developer.
**Scenario:**
- Someone added hardcoded login credentials (`username: "admin"`, `password: "admin123"`) to `app.py`
- This is a critical security issue
- You need to identify who made this change so the team can discuss it with them
**Suggested Approach:**
1. Navigate to the challenge directory: `cd challenge`
2. Open `investigation.md` to see the questions
3. Examine `app.py` to find the suspicious line
4. Use `git blame` to find who wrote that line
5. Use `git blame -e` to see email addresses
6. Use `git show` to see the full commit details
7. Document your findings in `investigation.md`
> **Important Notes:**
> - `git blame` shows who last modified each line
> - Each line shows: commit hash, author, date, line number, and content
> - Use `-e` flag to show email addresses
> - Use `-L` to focus on specific line ranges
## Key Concepts
- **Git Blame**: Shows the revision and author who last modified each line of a file
- **Code Archaeology**: Using Git history to understand when and why code changed
- **Author Attribution**: Identifying who wrote specific code for context, not punishment
- **Commit Context**: Understanding the full story behind a change
## Understanding Git Blame Output
When you run `git blame app.py`, you'll see output like this:
```
a1b2c3d4 (John Doe 2024-01-15 10:30:45 +0000 1) # app.py - Main application
a1b2c3d4 (John Doe 2024-01-15 10:30:45 +0000 2)
e5f6g7h8 (Jane Smith 2024-01-16 14:20:10 +0000 3) from auth import login
e5f6g7h8 (Jane Smith 2024-01-16 14:20:10 +0000 4)
i9j0k1l2 (Bob Wilson 2024-01-17 09:15:30 +0000 5) def main():
i9j0k1l2 (Bob Wilson 2024-01-17 09:15:30 +0000 6) login("admin", "admin123")
```
### Breaking It Down
Each line shows:
1. **Commit Hash** (`a1b2c3d4`) - The commit that last changed this line
2. **Author Name** (`John Doe`) - Who made the change
3. **Date/Time** (`2024-01-15 10:30:45 +0000`) - When it was changed
4. **Line Number** (`1`) - The line number in the current file
5. **Line Content** (`# app.py - Main application`) - The actual code
### Useful Git Blame Options
```bash
git blame <file> # Basic blame output
git blame -e <file> # Show email addresses instead of names
git blame -L 10,20 <file> # Only show lines 10-20
git blame -L 10,+5 <file> # Show 5 lines starting from line 10
git blame -w <file> # Ignore whitespace changes
git blame <commit> <file> # Blame as of specific commit
```
### Following Up After Blame
Once you find the commit hash:
```bash
git show <commit-hash> # See the full commit details
git log -p <commit-hash> # See commit with diff
git show <commit-hash> --stat # See which files were changed
```
## When to Use Git Blame
**Good reasons to use `git blame`:**
- 🔍 Understanding why code was written a certain way
- 📚 Finding context for a piece of code
- 🐛 Identifying when a bug was introduced
- 💡 Discovering the thought process behind a decision
- 👥 Finding who to ask about specific code
**Not for blaming:**
- ❌ Finding someone to blame for mistakes
- ❌ Tracking "productivity" or code ownership
- ❌ Punishing developers for old code
**Remember:** Code archaeology is about understanding, not blaming!
## Useful Commands
### Investigation Commands
```bash
# Find who changed each line
git blame <file>
git blame -e <file> # With email addresses
# Focus on specific lines
git blame -L 10,20 <file> # Lines 10-20
git blame -L :function_name <file> # Specific function (Git 2.20+)
# See historical blame
git blame <commit>^ <file> # Blame before a specific commit
# Combine with grep
git blame <file> | grep "pattern" # Find who wrote lines matching pattern
```
### Context Commands
```bash
# See full commit details
git show <commit-hash>
git log -1 <commit-hash> # Just the commit message
# See all commits by author
git log --author="name"
# See what else changed in that commit
git show <commit-hash> --stat
```
## Verification
Once you've completed your investigation in `investigation.md`, verify your solution:
```powershell
.\verify.ps1
```
The verification script will check that you've identified the correct developer.
## Need to Start Over?
If you want to reset the challenge and start fresh:
```powershell
.\reset.ps1
```
This will remove the challenge directory and run the setup script again, giving you a clean slate.

View File

@@ -0,0 +1,24 @@
#!/usr/bin/env pwsh
<#
.SYNOPSIS
Resets the Module 05 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 05 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,323 @@
#!/usr/bin/env pwsh
<#
.SYNOPSIS
Sets up the Module 05 challenge environment for git blame investigation.
.DESCRIPTION
This script creates a challenge directory with a Git repository that
contains a security vulnerability (hardcoded credentials) for students
to investigate using git blame.
#>
Write-Host "`n=== Setting up Module 05 Challenge ===" -ForegroundColor Cyan
# Remove existing challenge directory if it exists
if (Test-Path "challenge") {
Write-Host "Removing existing challenge directory..." -ForegroundColor Yellow
Remove-Item -Recurse -Force "challenge"
}
# Create fresh challenge directory
Write-Host "Creating challenge directory..." -ForegroundColor Green
New-Item -ItemType Directory -Path "challenge" | Out-Null
Set-Location "challenge"
# Initialize Git repository
Write-Host "Initializing Git repository..." -ForegroundColor Green
git init | Out-Null
# Commit 1: Initial project structure (by Alice)
Write-Host "Creating initial project structure..." -ForegroundColor Green
git config user.name "Alice Johnson"
git config user.email "alice@example.com"
$appContent = @"
# app.py - Main application file
def main():
print("Welcome to My App!")
# Application initialization code here
pass
if __name__ == "__main__":
main()
"@
Set-Content -Path "app.py" -Value $appContent
git add .
git commit -m "Initial project structure" | Out-Null
# Commit 2: Add authentication module (by Bob)
Write-Host "Adding authentication module..." -ForegroundColor Green
git config user.name "Bob Chen"
git config user.email "bob@example.com"
$authContent = @"
# auth.py - Authentication module
def login(username, password):
# Authenticate user
print(f"Logging in user: {username}")
return True
def logout(username):
# Log out user
print(f"Logging out user: {username}")
return True
"@
Set-Content -Path "auth.py" -Value $authContent
$appContent = @"
# app.py - Main application file
from auth import login, logout
def main():
print("Welcome to My App!")
# Application initialization code here
pass
if __name__ == "__main__":
main()
"@
Set-Content -Path "app.py" -Value $appContent
git add .
git commit -m "Add authentication module" | Out-Null
# Commit 3: Add database connection (by Carol)
Write-Host "Adding database connection..." -ForegroundColor Green
git config user.name "Carol Martinez"
git config user.email "carol@example.com"
$databaseContent = @"
# database.py - Database connection module
def connect():
# Connect to database
print("Connecting to database...")
return True
def disconnect():
# Disconnect from database
print("Disconnecting from database...")
return True
"@
Set-Content -Path "database.py" -Value $databaseContent
$appContent = @"
# app.py - Main application file
from auth import login, logout
from database import connect, disconnect
def main():
print("Welcome to My App!")
connect()
# Application initialization code here
pass
if __name__ == "__main__":
main()
"@
Set-Content -Path "app.py" -Value $appContent
git add .
git commit -m "Add database connection" | Out-Null
# Commit 4: Add hardcoded credentials (THE SECURITY ISSUE - by Suspicious Developer)
Write-Host "Adding suspicious change..." -ForegroundColor Green
git config user.name "Suspicious Developer"
git config user.email "guilty@email.com"
$appContent = @"
# app.py - Main application file
from auth import login, logout
from database import connect, disconnect
def main():
print("Welcome to My App!")
connect()
# Quick fix for testing - TODO: Remove before production!
if login("admin", "admin123"):
print("Admin logged in successfully")
pass
if __name__ == "__main__":
main()
"@
Set-Content -Path "app.py" -Value $appContent
git add .
git commit -m "Add quick test login for debugging" | Out-Null
# Commit 5: Add logging (by David - innocent commit after the security issue)
Write-Host "Adding logging module..." -ForegroundColor Green
git config user.name "David Lee"
git config user.email "david@example.com"
$loggingContent = @"
# logging_config.py - Logging configuration
import logging
def setup_logging():
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
return logging.getLogger(__name__)
"@
Set-Content -Path "logging_config.py" -Value $loggingContent
git add .
git commit -m "Add logging configuration" | Out-Null
# Reset git config
git config user.name "Workshop Student"
git config user.email "student@example.com"
# Create investigation.md template
Write-Host "Creating investigation template..." -ForegroundColor Green
$investigationTemplate = @"
# Security Investigation Report
## Incident Overview
A security vulnerability has been discovered in the codebase: hardcoded credentials in `app.py`.
**Your task:** Use git blame and related Git commands to investigate this security issue and document your findings.
---
## Question 1: What line number contains the hardcoded password?
Look at `app.py` and find the line with `"admin123"`.
**Your Answer:**
<!-- Write the line number here -->
---
## Question 2: Who added the hardcoded credentials?
Use `git blame` to find the email address of the developer who wrote the line with the hardcoded credentials.
**Suggested commands:**
``````bash
# View blame with email addresses
git blame -e app.py
# Or focus on specific lines (if you know the line range)
git blame -L 8,10 app.py
# Look for the line containing login("admin", "admin123")
``````
**Your Answer (provide the email address):**
<!-- Write the email address here -->
---
## Question 3: What was the commit message for the change that introduced the hardcoded credentials?
Once you've found the commit hash from git blame, use `git show` or `git log` to see the full commit message.
**Suggested commands:**
``````bash
# After finding the commit hash from git blame
git show <commit-hash>
git log -1 <commit-hash>
``````
**Your Answer:**
<!-- Write the commit message here -->
---
## Question 4: How many files were modified in the commit that added the hardcoded credentials?
Use `git show` with the `--stat` flag to see which files were changed.
**Suggested commands:**
``````bash
git show <commit-hash> --stat
git show <commit-hash> --name-only
``````
**Your Answer:**
<!-- Write the number or list the files here -->
---
## Question 5: When was this security vulnerability introduced?
Use the timestamp from git blame to determine when the vulnerable code was committed.
**Your Answer (date and time):**
<!-- Write the date/time here -->
---
## Recommendations
Based on your investigation, what actions should the team take?
**Your Recommendations:**
<!-- Write your recommendations here, for example:
- Remove hardcoded credentials
- Implement proper environment variables
- Add pre-commit hooks to prevent secrets
- Review with the developer who made the change
-->
---
## Quick Reference - Investigation Commands
**Finding Who Changed What:**
``````bash
git blame <file> # Show who last modified each line
git blame -e <file> # Show with email addresses
git blame -L 10,20 <file> # Blame specific line range
``````
**Getting Commit Details:**
``````bash
git show <commit-hash> # See full commit details
git show <commit-hash> --stat # See files changed
git log -1 <commit-hash> # See commit message only
git log -p <commit-hash> # See commit with diff
``````
**Searching History:**
``````bash
git log --all --grep="keyword" # Search commit messages
git log --author="name" # See commits by author
git log --since="2 weeks ago" # Recent commits
``````
---
When you're done with your investigation, run ``..\verify.ps1`` to check your answers!
"@
Set-Content -Path "investigation.md" -Value $investigationTemplate
# Return to module directory
Set-Location ..
Write-Host "`n=== Setup Complete! ===" -ForegroundColor Green
Write-Host "`nYour investigation environment is ready in the 'challenge/' directory." -ForegroundColor Cyan
Write-Host "`nScenario: Someone committed hardcoded credentials to app.py!" -ForegroundColor Yellow
Write-Host "`nNext steps:" -ForegroundColor Cyan
Write-Host " 1. cd challenge" -ForegroundColor White
Write-Host " 2. Open 'investigation.md' to see the investigation questions" -ForegroundColor White
Write-Host " 3. Use 'git blame -e app.py' to start your investigation" -ForegroundColor White
Write-Host " 4. Fill in your findings in 'investigation.md'" -ForegroundColor White
Write-Host " 5. Run '..\verify.ps1' to check your investigation" -ForegroundColor White
Write-Host ""

View File

@@ -0,0 +1,114 @@
#!/usr/bin/env pwsh
<#
.SYNOPSIS
Verifies the Module 05 challenge solution.
.DESCRIPTION
This script checks that:
- The challenge directory exists
- A Git repository exists
- investigation.md exists with correct findings about the security issue
#>
Write-Host "`n=== Verifying Module 05 Solution ===" -ForegroundColor Cyan
$allChecksPassed = $true
# Check if challenge directory exists
if (-not (Test-Path "challenge")) {
Write-Host "[FAIL] Challenge directory not found. Did you run setup.ps1?" -ForegroundColor Red
exit 1
}
Set-Location "challenge"
# Check if git repository exists
if (-not (Test-Path ".git")) {
Write-Host "[FAIL] Not a git repository. Did you run setup.ps1?" -ForegroundColor Red
Set-Location ..
exit 1
}
# Check if investigation.md exists
if (-not (Test-Path "investigation.md")) {
Write-Host "[FAIL] investigation.md not found. Did you run setup.ps1?" -ForegroundColor Red
Write-Host "[HINT] The setup script should have created investigation.md for you" -ForegroundColor Yellow
$allChecksPassed = $false
} else {
Write-Host "[PASS] investigation.md exists" -ForegroundColor Green
# Read the investigation file
$investigation = Get-Content "investigation.md" -Raw
$investigationLower = $investigation.ToLower()
# Check 1: Line number (line 8 contains the hardcoded password)
if ($investigationLower -match "8") {
Write-Host "[PASS] Correct line number identified" -ForegroundColor Green
} else {
Write-Host "[FAIL] Line number not found or incorrect" -ForegroundColor Red
Write-Host "[HINT] Look at app.py to find which line contains 'admin123'" -ForegroundColor Yellow
$allChecksPassed = $false
}
# Check 2: Email address (guilty@email.com)
if ($investigationLower -match "guilty@email\.com") {
Write-Host "[PASS] Correct email address found using git blame!" -ForegroundColor Green
} else {
Write-Host "[FAIL] Developer's email address not found" -ForegroundColor Red
Write-Host "[HINT] Use 'git blame -e app.py' to see who changed each line with email addresses" -ForegroundColor Yellow
$allChecksPassed = $false
}
# Check 3: Commit message (contains "test" or "debug" or "quick")
if ($investigationLower -match "test|debug|quick") {
Write-Host "[PASS] Commit message identified" -ForegroundColor Green
} else {
Write-Host "[FAIL] Commit message not found" -ForegroundColor Red
Write-Host "[HINT] Use 'git show <commit-hash>' to see the commit message" -ForegroundColor Yellow
$allChecksPassed = $false
}
# Check 4: Number of files (1 file - only app.py)
if ($investigationLower -match "1|one|app\.py") {
Write-Host "[PASS] Number of files modified identified" -ForegroundColor Green
} else {
Write-Host "[FAIL] Number of files modified not found" -ForegroundColor Red
Write-Host "[HINT] Use 'git show <commit-hash> --stat' to see which files were changed" -ForegroundColor Yellow
$allChecksPassed = $false
}
# Check 5: Some mention of timestamp/date (flexible check)
# We're just checking they attempted to answer this
if ($investigationLower -match "202|date|time|\d{4}-\d{2}-\d{2}") {
Write-Host "[PASS] Timestamp/date documented" -ForegroundColor Green
} else {
Write-Host "[FAIL] Timestamp/date not documented" -ForegroundColor Red
Write-Host "[HINT] The git blame output shows the date and time of each change" -ForegroundColor Yellow
$allChecksPassed = $false
}
}
Set-Location ..
# Final summary
if ($allChecksPassed) {
Write-Host "`n" -NoNewline
Write-Host "=====================================" -ForegroundColor Green
Write-Host " INVESTIGATION COMPLETE!" -ForegroundColor Green
Write-Host "=====================================" -ForegroundColor Green
Write-Host "`nExcellent detective work! You've successfully used git blame to track down the security issue." -ForegroundColor Cyan
Write-Host "`nYou now know how to:" -ForegroundColor Cyan
Write-Host " - Use git blame to find who modified each line" -ForegroundColor White
Write-Host " - Read and interpret git blame output" -ForegroundColor White
Write-Host " - Use git blame with -e flag to show email addresses" -ForegroundColor White
Write-Host " - Find commit details after identifying changes with blame" -ForegroundColor White
Write-Host " - Conduct code archaeology to understand code history" -ForegroundColor White
Write-Host "`nRemember: git blame is for understanding, not blaming!" -ForegroundColor Yellow
Write-Host "`nReady for the next module!" -ForegroundColor Green
Write-Host ""
} else {
Write-Host "`n[SUMMARY] Some checks failed. Review the hints above and try again." -ForegroundColor Red
Write-Host "[INFO] You can run this verification script as many times as needed." -ForegroundColor Yellow
Write-Host ""
exit 1
}

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
}

493
GIT-CHEATSHEET.md Normal file
View File

@@ -0,0 +1,493 @@
# Git Command Cheatsheet
A comprehensive reference for all Git commands covered in this workshop.
---
## Essentials
### Repository Initialization
```bash
git init
```
Initialize a new Git repository in the current directory.
### Staging & Committing
```bash
git status
```
Show the current state of your working directory and staging area.
```bash
git add <file>
```
Add a file to the staging area (prepare it for commit).
```bash
git add .
```
Add all changed files in the current directory to the staging area.
```bash
git commit -m "message"
```
Create a new commit with the staged changes and a descriptive message.
```bash
git commit -am "message"
```
Stage all modified tracked files and commit in one step (doesn't include new files).
### Viewing History
```bash
git log
```
Show the commit history for the current branch.
```bash
git log --oneline
```
Show commit history in compact one-line format.
```bash
git log --oneline --graph --all
```
Show commit history as a graph with all branches.
```bash
git log --stat
```
Show commit history with statistics about which files changed.
```bash
git show <commit>
```
Display detailed information about a specific commit.
```bash
git show <commit>:<file>
```
View the contents of a file from a specific commit.
### Comparing Changes
```bash
git diff
```
Show unstaged changes in your working directory.
```bash
git diff --staged
```
Show staged changes (what will be committed).
```bash
git diff <commit1> <commit2>
```
Compare two commits.
```bash
git diff <commit1> <commit2> <file>
```
Compare changes to a specific file between two commits.
**Understanding diff output:**
- Lines with `+` (green) = added
- Lines with `-` (red) = removed
- Lines with ` ` (space) = unchanged (context)
- `@@ -1,5 +1,7 @@` = location of changes (old line/count, new line/count)
For a detailed guide on reading diff output, see Module 02 README.md.
### Branching
```bash
git branch
```
List all local branches (current branch marked with *).
```bash
git branch <branch-name>
```
Create a new branch.
```bash
git branch -d <branch-name>
```
Delete a branch (only if it's been merged).
```bash
git branch -D <branch-name>
```
Force delete a branch (even if not merged).
```bash
git switch <branch-name>
```
Switch to a different branch.
```bash
git switch -c <branch-name>
```
Create a new branch and switch to it in one command.
```bash
git switch -
```
Switch back to the previous branch.
### Merging
```bash
git merge <branch-name>
```
Merge the specified branch into your current branch.
```bash
git merge --abort
```
Abort a merge in progress and return to the pre-merge state.
### Merge Conflict Resolution
```bash
git status
```
During a merge conflict, shows which files have conflicts.
```bash
# After resolving conflicts manually:
git add <resolved-file>
git commit
```
Stage resolved files and complete the merge.
### Restoring Files
```bash
git restore <file>
```
Discard changes in working directory (restore from last commit).
```bash
git restore --staged <file>
```
Unstage a file (remove from staging area but keep changes).
```bash
git restore --source=<commit> <file>
```
Restore a file to its state in a specific commit.
---
## Advanced
### Rebasing
```bash
git rebase <branch>
```
Reapply your commits on top of another branch (creates linear history).
```bash
git rebase -i <commit>
```
Interactive rebase - edit, reorder, squash, or drop commits.
```bash
git rebase --continue
```
Continue rebasing after resolving conflicts.
```bash
git rebase --abort
```
Abort a rebase and return to the original state.
### Reset (History Manipulation)
```bash
git reset --soft HEAD~<n>
```
Undo last n commits but keep changes staged.
```bash
git reset --mixed HEAD~<n>
```
Undo last n commits and unstage changes (default mode).
```bash
git reset --hard HEAD~<n>
```
Undo last n commits and discard all changes (DESTRUCTIVE).
```bash
git reset --hard <commit>
```
Reset your branch to a specific commit, discarding all changes.
### Revert (Safe Undo)
```bash
git revert <commit>
```
Create a new commit that undoes changes from a specific commit (safe for shared branches).
```bash
git revert --no-commit <commit>
```
Revert changes but don't create the commit yet (allows editing).
```bash
git revert --abort
```
Abort a revert in progress.
### Cherry-Pick
```bash
git cherry-pick <commit>
```
Apply the changes from a specific commit to your current branch.
```bash
git cherry-pick <commit1> <commit2> <commit3>
```
Cherry-pick multiple commits.
```bash
git cherry-pick --continue
```
Continue cherry-picking after resolving conflicts.
```bash
git cherry-pick --abort
```
Abort a cherry-pick in progress.
### Stash (Temporary Storage)
```bash
git stash
```
Save your uncommitted changes temporarily and revert to a clean working directory.
```bash
git stash save "description"
```
Stash changes with a descriptive message.
```bash
git stash list
```
Show all stashed changes.
```bash
git stash pop
```
Apply the most recent stash and remove it from the stash list.
```bash
git stash apply
```
Apply the most recent stash but keep it in the stash list.
```bash
git stash apply stash@{n}
```
Apply a specific stash by index.
```bash
git stash drop
```
Delete the most recent stash.
```bash
git stash drop stash@{n}
```
Delete a specific stash.
```bash
git stash clear
```
Delete all stashes.
### Working with Remotes
```bash
git clone <url>
```
Create a local copy of a remote repository.
```bash
git remote
```
List all remote repositories.
```bash
git remote -v
```
List all remote repositories with their URLs.
```bash
git remote add <name> <url>
```
Add a new remote repository.
```bash
git remote remove <name>
```
Remove a remote repository.
```bash
git fetch <remote>
```
Download changes from remote but don't merge them.
```bash
git pull <remote> <branch>
```
Fetch and merge changes from remote branch to current branch.
```bash
git pull
```
Fetch and merge from the tracked remote branch.
```bash
git push <remote> <branch>
```
Upload your commits to a remote branch.
```bash
git push -u <remote> <branch>
```
Push and set up tracking relationship (use -u first time pushing a branch).
```bash
git push
```
Push to the tracked remote branch.
### Worktrees (Multiple Working Directories)
```bash
git worktree add <path> <branch>
```
Create a new working directory linked to your repository for a specific branch.
```bash
git worktree add <path> -b <new-branch>
```
Create a new branch and working directory for it.
```bash
git worktree list
```
Show all worktrees associated with the repository.
```bash
git worktree remove <path>
```
Remove a worktree.
```bash
git worktree prune
```
Clean up stale worktree administrative data.
### Bisect (Binary Search for Bugs)
```bash
git bisect start
```
Start a bisect session to find which commit introduced a bug.
```bash
git bisect bad
```
Mark the current commit as bad (contains the bug).
```bash
git bisect good <commit>
```
Mark a commit as good (doesn't contain the bug).
```bash
git bisect reset
```
End the bisect session and return to the original branch.
```bash
git bisect run <script>
```
Automatically test commits using a script to find the bad commit.
---
## Useful Tips
### Viewing Changes
- Use `git log --oneline --graph --all` to visualize branch structure
- Use `git log -p` to show the actual changes (patch) in each commit
- Use `git log --author="name"` to filter commits by author
- Use `git log --since="2 weeks ago"` to filter commits by date
### Undoing Changes
- **Haven't committed yet?** Use `git restore <file>` to discard changes
- **Staged but want to unstage?** Use `git restore --staged <file>`
- **Committed to wrong branch?** Use `git cherry-pick` to copy the commit to the right branch
- **Want to change last commit message?** Use `git commit --amend`
- **Pushed to remote already?** Use `git revert` (not reset) to safely undo
### Best Practices
- Commit often with clear, descriptive messages
- Pull before you push to avoid conflicts
- Use branches for new features or experiments
- Don't rebase or reset commits that have been pushed to shared branches
- Use `git stash` when you need to switch contexts quickly
- Test your changes before committing
### Getting Help
```bash
git help <command>
```
Show detailed help for any Git command.
```bash
git <command> --help
```
Alternative way to show help.
```bash
git <command> -h
```
Show brief usage summary.
---
## Module Reference
Each workshop module focuses on specific commands:
- **Module 01**: init, add, commit, status
- **Module 02**: log, show, diff
- **Module 03**: branch, switch
- **Module 04**: merge (fast-forward and three-way)
- **Module 05**: merge (conflict resolution)
- **Module 06**: rebase
- **Module 07**: reset (interactive rebase alternative)
- **Module 08**: cherry-pick
- **Module 09**: reset vs revert
- **Module 10**: stash
- **Module 11**: clone, remote, push, pull, fetch
- **Module 12**: worktree
- **Module 13**: bisect

442
README.md
View File

@@ -1,37 +1,157 @@
# Git Workshop
A hands-on, progressive git workshop with practical challenges ranging from basic to advanced concepts.
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, collaboration, and advanced Git techniques.
## Workshop Structure
Each module is a self-contained challenge that teaches specific git concepts:
The workshop is organized into two tracks:
- **Module 01**: Git Basics (init, add, commit, status)
- **Module 02**: Viewing History (log, diff)
- **Module 03**: Branching Basics
- **Module 04**: Merging
- **Module 05**: Merge Conflicts
- **Module 06**: Rebasing
- **Module 07**: Interactive Rebase
- **Module 08**: Cherry-pick
- **Module 09**: Reset vs Revert
- **Module 10**: Stash
- **Module 11**: Working with Remotes
### 01 Essentials - Core Git Skills (9 modules)
Master fundamental Git concepts and collaborative workflows:
- **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
- **Module 04: Merging** - Combine branches and understand merge workflows
- **Module 05: Merge Conflicts** - Identify, understand, and resolve merge conflicts step-by-step
- **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
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
3. Run `./setup.ps1` to create the challenge environment
4. Complete the challenge using git commands
5. Run `./verify.ps1` to check if you've solved it correctly
6. Move to the next module
**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
Most modules include setup, verification, and reset scripts. Here's how to run them:
### Windows
If you encounter an "execution policy" error when running scripts, open PowerShell as Administrator and run:
```powershell
Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser
```
Then run scripts using:
```powershell
.\setup.ps1
```
### macOS/Linux
First, make sure scripts are executable (only needed once):
```bash
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:
```bash
./setup.ps1
```
Or explicitly with PowerShell:
```bash
pwsh setup.ps1
```
**Note:** All examples in this workshop use Windows syntax (`.\script.ps1`). macOS/Linux users should use `./script.ps1` instead.
## Requirements
- Git installed and configured
- PowerShell (Windows PowerShell 5.1+ or PowerShell Core 7+)
- Basic command line knowledge
### Git
You need Git version 2.23 or later (for `git switch` and `git restore` commands).
**Check if Git is installed:**
```powershell
git --version
```
If you see a version number (e.g., "git version 2.34.1"), you're all set!
If not, download and install from: https://git-scm.com/downloads
**Configure Git (Required - First Time Only):**
Before making your first commit, tell Git who you are:
```powershell
git config --global user.name "Your Name"
git config --global user.email "your.email@example.com"
```
**Verify your configuration:**
```powershell
git config --global user.name
git config --global user.email
```
You should see your name and email printed. This is required to make commits in Git.
### PowerShell
- **Windows**: PowerShell 5.1+ (already included in Windows 10/11)
- **macOS/Linux**: Install PowerShell Core 7+ from https://github.com/PowerShell/PowerShell
**Check PowerShell version:**
```powershell
$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
You should know how to:
- Navigate directories (`cd`, `ls` or `dir`)
- See your current location (`pwd`)
- Read text files (`cat` or `type`)
Don't worry if you're not an expert - we'll guide you through each step!
## Optional: Install Glow for Pretty Markdown
@@ -55,20 +175,130 @@ After installation, read markdown files with:
Without glow, you can still read markdown files with any text editor or `cat README.md`.
## Common Git Terms
New to Git? Here are the key terms you'll encounter:
- **Repository (or "repo")**: A project folder tracked by Git, containing your files and their complete history
- **Commit**: A snapshot of your project at a specific point in time (like a save point in a video game)
- **Staging Area (or "Index")**: A preparation area where you select which changes to include in your next commit
- **Working Directory**: The actual files you see and edit on your computer
- **Branch**: An independent line of development (like a parallel universe for your code)
- **HEAD**: A pointer showing which commit you're currently working from
- **main/master**: The primary branch in a repository (main is the modern convention)
- **Merge**: Combining changes from different branches
- **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!
## Getting Started
Start with Module 01:
Start with Essentials Module 01:
```powershell
cd module-01-basics
cd 01_essentials/01-basics
.\setup.ps1
```
Follow the instructions in each module's README.md file.
## What Makes This Workshop Different
- **Hands-On Practice**: Each module creates a real Git scenario you must solve
- **Automated Verification**: Scripts check your solution instantly (modules 01-08)
- **Progressive Difficulty**: Builds from basics to advanced Git techniques
- **Reset Anytime**: Each local module includes a reset script for a fresh start
- **Self-Paced**: Learn at your own speed with detailed README guides
- **Real Collaboration**: Module 09 uses an actual Git server for authentic teamwork
- **Comprehensive Coverage**: From `git init` to advanced rebasing and bisecting
## Learning Path
The modules are designed to build on each other:
### Recommended Progression
**Phase 1: Core Fundamentals (Essentials 01-03)**
- Git Basics, History, Branching
- **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
- **Don't skip modules** - each builds on previous concepts
- **Read the README.md thoroughly** before starting each challenge
- **Keep [GIT-CHEATSHEET.md](GIT-CHEATSHEET.md) open** as a quick reference
- **Experiment freely** - you can always run `./reset.ps1` to start over
- **Use `git log --oneline --graph --all`** frequently to visualize repository state
- **If stuck**, check the Key Concepts section in the module's README
- **Consider installing glow** for better markdown reading experience
- **For Module 09**, work with a partner - collaboration is the point!
## Skills You'll Master
By completing this workshop, you'll be able to:
### From Essentials Track:
- ✅ Create and manage Git repositories with confidence
- ✅ Navigate project history and understand what changed when
- ✅ Use branches effectively for parallel development
- ✅ Merge branches and resolve conflicts like a pro
- ✅ Apply specific commits with cherry-pick
- ✅ Choose correctly between reset and revert
- ✅ Use stash to manage work-in-progress without commits
-**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
- ✅ 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.
## 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
```powershell
@@ -76,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`
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! 🚀

View File

@@ -1,84 +0,0 @@
# Module 02: Viewing History
## Learning Objectives
In this module, you will:
- Understand commit history and how to navigate it
- Use `git log` to view commit history with various formats
- Use `git show` to view specific commit details
- Use `git diff` to compare changes between commits
- Understand commit hashes and references
## Challenge
### Setup
Run the setup script to create your challenge environment:
```powershell
.\setup.ps1
```
This will create a `challenge/` directory with a Git repository that already has some commit history.
### Your Task
You'll explore an existing Git repository that contains multiple commits. Your goal is to use Git commands to discover information about the repository's history.
The setup script will create an `answers.md` file in the challenge directory with questions for you to answer. Fill in your answers directly in that file.
**Suggested Approach:**
1. Navigate to the challenge directory: `cd challenge`
2. Open `answers.md` to see the questions
3. View the commit history: `git log`
4. Try different log formats: `git log --oneline`, `git log --stat`
5. View specific commits: `git show <commit-hash>`
6. Compare commits: `git diff <commit1> <commit2>`
7. Fill in your answers in `answers.md`
> **Important Notes:**
> - You can use any Git commands you like to explore the repository
> - Fill in your answers directly in the `answers.md` file (there are placeholder sections for each answer)
> - Try different `git log` options to see which format you prefer
> - Commit hashes can be referenced by their full hash or just the first 7 characters
## Key Concepts
- **Commit Hash**: A unique identifier (SHA-1 hash) for each commit. You can use the full hash or just the first few characters.
- **Commit Message**: A description of what changed in that commit, written by the author.
- **Commit History**: The chronological record of all changes made to a repository.
- **HEAD**: A pointer to the current commit you're working from.
## Useful Commands
```bash
git log # View commit history
git log --oneline # Compact one-line format
git log --stat # Show files changed in each commit
git log --graph # Show branch graph (more useful with branches)
git show <commit> # View specific commit details
git show <commit>:<file> # View a file from a specific commit
git diff <commit1> <commit2> # Compare two commits
git diff <commit> # Compare commit with current working directory
```
## Verification
Once you've filled in your answers in `answers.md`, verify your solution:
```powershell
.\verify.ps1
```
The verification script will check that your answers contain the expected information.
## Need to Start Over?
If you want to reset the challenge and start fresh:
```powershell
.\reset.ps1
```
This will remove the challenge directory and run the setup script again, giving you a clean slate.

View File

@@ -1,97 +0,0 @@
# Module 04: Merging
## Learning Objectives
In this module, you will:
- Understand what merging means in Git
- Perform a fast-forward merge
- Perform a three-way merge
- Understand when merge commits are created
- Use `git merge` to combine branches
## Challenge
### Setup
Run the setup script to create your challenge environment:
```powershell
.\setup.ps1
```
This will create a `challenge/` directory with a Git repository that has a main branch and a feature branch ready to merge.
### Your Task
This challenge has two parts that teach you about the two types of merges in Git:
**Part 1: Fast-Forward Merge**
1. Merge the existing `feature-api` branch into main
2. Observe that this is a "fast-forward" merge (no merge commit created)
**Part 2: Three-Way Merge**
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`
2. Check current branch: `git branch` (should be on main)
3. View existing branches: `git branch -a`
4. Merge feature-api: `git merge feature-api`
5. View the log: `git log --oneline --graph`
6. Create feature-ui branch: `git checkout -b 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 checkout 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:**
> - 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
- **Merge**: Combining changes from different branches into one 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.
- **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.
## Useful Commands
```bash
git merge <branch> # Merge branch into current branch
git log --oneline --graph # View merge history visually
git log --graph --all # View all branches and merges
git log --merges # Show only merge commits
git branch -d <branch> # Delete a merged branch (optional)
```
## Verification
Once you've completed both merges, verify your solution:
```powershell
.\verify.ps1
```
The verification script will check that you've successfully merged both feature branches and understand the different merge types.
## Need to Start Over?
If you want to reset the challenge and start fresh:
```powershell
.\reset.ps1
```
This will remove the challenge directory and run the setup script again, giving you a clean slate.

View File

@@ -1,128 +0,0 @@
#!/usr/bin/env pwsh
<#
.SYNOPSIS
Sets up the Module 04 challenge environment for learning about merging.
.DESCRIPTION
This script creates a challenge directory with a Git repository that
contains a main branch and a feature branch ready to merge. Students
will practice both fast-forward and three-way merges.
#>
Write-Host "`n=== Setting up Module 04 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"
# Commit 1: Initial API file on main
Write-Host "Creating initial API structure on main..." -ForegroundColor Green
$apiContent = @"
# api.py - API module
def api_handler():
print("API Handler initialized")
return True
"@
Set-Content -Path "api.py" -Value $apiContent
git add .
git commit -m "Initial API setup" | Out-Null
# Commit 2: Add more API functionality on main
$apiContent = @"
# api.py - API module
def api_handler():
print("API Handler initialized")
return True
def get_data():
print("Fetching data from API...")
return {"status": "ok"}
"@
Set-Content -Path "api.py" -Value $apiContent
git add .
git commit -m "Add get_data function" | Out-Null
# Create feature-api branch and add commits
Write-Host "Creating feature-api branch..." -ForegroundColor Green
git checkout -b feature-api 2>$null | Out-Null
# Commit on feature-api: Add API routes
$routesContent = @"
# api-routes.py - API Routes module
def setup_routes():
print("Setting up API routes...")
routes = {
"/api/data": "get_data",
"/api/status": "get_status"
}
return routes
"@
Set-Content -Path "api-routes.py" -Value $routesContent
git add .
git commit -m "Add API routes" | Out-Null
# Second commit on feature-api: Enhance routes
$routesContent = @"
# api-routes.py - API Routes module
def setup_routes():
print("Setting up API routes...")
routes = {
"/api/data": "get_data",
"/api/status": "get_status",
"/api/health": "health_check"
}
return routes
def health_check():
return {"healthy": True}
"@
Set-Content -Path "api-routes.py" -Value $routesContent
git add .
git commit -m "Add health check route" | Out-Null
# Switch back to main branch
Write-Host "Switching back to main branch..." -ForegroundColor Green
git checkout main 2>$null | 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:" -ForegroundColor Cyan
Write-Host " - A main branch with API code" -ForegroundColor White
Write-Host " - A feature-api branch with API routes ready to merge" -ForegroundColor White
Write-Host "`nNext steps:" -ForegroundColor Cyan
Write-Host " 1. cd challenge" -ForegroundColor White
Write-Host " 2. View branches: git branch -a" -ForegroundColor White
Write-Host " 3. Merge feature-api: git merge feature-api (fast-forward merge)" -ForegroundColor White
Write-Host " 4. Create feature-ui branch: git checkout -b feature-ui" -ForegroundColor White
Write-Host " 5. Make commits on feature-ui" -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 ""

View File

@@ -1,140 +0,0 @@
#!/usr/bin/env pwsh
<#
.SYNOPSIS
Verifies the Module 04 challenge solution.
.DESCRIPTION
This script checks that:
- The challenge directory exists
- A Git repository exists
- Currently on main branch
- feature-api has been merged into main
- feature-ui branch exists and has been merged
- A merge commit exists (from three-way merge)
#>
Write-Host "`n=== Verifying Module 04 Solution ===" -ForegroundColor Cyan
$allChecksPassed = $true
# Check if challenge directory exists
if (-not (Test-Path "challenge")) {
Write-Host "[FAIL] Challenge directory not found. Did you run setup.ps1?" -ForegroundColor Red
exit 1
}
Set-Location "challenge"
# Check if git repository exists
if (-not (Test-Path ".git")) {
Write-Host "[FAIL] Not a git repository. Did you run setup.ps1?" -ForegroundColor Red
Set-Location ..
exit 1
}
# Check current branch is main
$currentBranch = git branch --show-current 2>$null
if ($currentBranch -eq "main") {
Write-Host "[PASS] Currently on main branch" -ForegroundColor Green
} else {
Write-Host "[FAIL] Not on main branch (currently on: $currentBranch)" -ForegroundColor Red
Write-Host "[HINT] Switch to main with: git checkout main" -ForegroundColor Yellow
$allChecksPassed = $false
}
# Check if feature-api branch exists
$featureApiBranch = git branch --list "feature-api" 2>$null
if ($featureApiBranch) {
Write-Host "[PASS] Branch 'feature-api' exists" -ForegroundColor Green
} else {
Write-Host "[INFO] Branch 'feature-api' not found (may have been deleted after merge)" -ForegroundColor Cyan
}
# Check if commits from feature-api are in main
$apiRoutesCommit = git log --all --grep="Add API routes" --oneline 2>$null
$healthCheckCommit = git log --all --grep="Add health check route" --oneline 2>$null
if ($apiRoutesCommit -and $healthCheckCommit) {
# Check if these commits are in main's history
$apiRoutesInMain = git log --grep="Add API routes" --oneline 2>$null
$healthCheckInMain = git log --grep="Add health check route" --oneline 2>$null
if ($apiRoutesInMain -and $healthCheckInMain) {
Write-Host "[PASS] Commits from feature-api are merged into main" -ForegroundColor Green
} else {
Write-Host "[FAIL] Commits from feature-api not found in main" -ForegroundColor Red
Write-Host "[HINT] Merge feature-api with: git merge feature-api" -ForegroundColor Yellow
$allChecksPassed = $false
}
} else {
Write-Host "[FAIL] Expected commits from feature-api not found" -ForegroundColor Red
Write-Host "[HINT] Did you run setup.ps1?" -ForegroundColor Yellow
$allChecksPassed = $false
}
# Check if api-routes.py exists (should be on main after merge)
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
if ($mergeCommits) {
Write-Host "[PASS] Merge commit(s) found (three-way merge completed)" -ForegroundColor Green
} else {
Write-Host "[FAIL] No merge commits found" -ForegroundColor Red
Write-Host "[HINT] Create divergence: make commits on both main and feature-ui, then merge" -ForegroundColor Yellow
$allChecksPassed = $false
}
# Check if ui.py exists (should be on main after merge)
if (Test-Path "ui.py") {
Write-Host "[PASS] File 'ui.py' exists on main (from feature-ui merge)" -ForegroundColor Green
} else {
Write-Host "[FAIL] File 'ui.py' not found on main" -ForegroundColor Red
Write-Host "[HINT] Create ui.py on feature-ui branch and merge it into main" -ForegroundColor Yellow
$allChecksPassed = $false
}
Set-Location ..
# Final summary
if ($allChecksPassed) {
Write-Host "`n" -NoNewline
Write-Host "=====================================" -ForegroundColor Green
Write-Host " CONGRATULATIONS! CHALLENGE PASSED!" -ForegroundColor Green
Write-Host "=====================================" -ForegroundColor Green
Write-Host "`nYou've successfully learned about Git merging!" -ForegroundColor Cyan
Write-Host "You now understand:" -ForegroundColor Cyan
Write-Host " - Fast-forward merges (when main hasn't changed)" -ForegroundColor White
Write-Host " - Three-way merges (when branches have diverged)" -ForegroundColor White
Write-Host " - How to use git merge to combine branches" -ForegroundColor White
Write-Host " - Merge commits and how to view them" -ForegroundColor White
Write-Host "`nReady for the next module!" -ForegroundColor Green
Write-Host ""
} else {
Write-Host "`n[SUMMARY] Some checks failed. Review the hints above and try again." -ForegroundColor Red
Write-Host "[INFO] You can run this verification script as many times as needed." -ForegroundColor Yellow
Write-Host ""
exit 1
}

View File

@@ -1,106 +0,0 @@
# Module 06: Merge Conflicts
## Learning Objectives
By the end of this module, you will:
- Understand what merge conflicts are and why they occur
- Identify merge conflicts in your repository
- Read and interpret conflict markers
- Resolve merge conflicts manually
- Complete a merge after resolving conflicts
## Challenge Description
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:
1. Attempt to merge the `update-config` branch into `main`
2. Identify and understand the merge conflict
3. Resolve the conflict by keeping both the timeout setting from `main` AND the debug setting from `update-config`
4. Complete the merge with an appropriate commit message
## Key Concepts
### What Are Merge Conflicts?
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.
### Conflict Markers
When a conflict occurs, Git adds special markers to the affected files:
```
<<<<<<< HEAD
Your current branch's changes
=======
The incoming branch's changes
>>>>>>> branch-name
```
- `<<<<<<< HEAD`: Marks the beginning of your current branch's version
- `=======`: Separates the two versions
- `>>>>>>> branch-name`: Marks the end of the incoming branch's version
### Resolving Conflicts
To resolve a 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
```bash
# Merge a branch (may result in conflicts)
git merge <branch-name>
# 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
```
## Verification
Run the verification script to check your solution:
```bash
.\verify.ps1
```
The verification will check that:
- The merge conflict was resolved
- The merge was completed successfully
- Both configuration settings were preserved correctly
- The commit history shows the merge
## Tips
- Don't panic when you see a conflict - it's a normal part of working with Git
- 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
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.

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 checkout -b add-feature`
6. Make changes to the project (add a new feature to app.js)
7. Commit your changes
8. Push to remote: `git push -u origin add-feature`
9. Simulate teammate changes (run the provided update script)
10. Fetch updates: `git fetch origin`
11. View remote changes: `git log origin/main`
12. Merge remote main into your branch: `git merge origin/main`
13. Run verification
## Tips
- `git clone` automatically sets up the remote as "origin"
- `git push -u` sets up tracking so future pushes can just use `git push`
- Use `git fetch` to see what's changed before merging
- `git pull` = `git fetch` + `git merge`
- Always pull before pushing to avoid conflicts
- Use `git branch -a` to see all local and remote branches
- Remote branches are read-only; you work on local branches
- `origin/main` is a remote tracking branch, `main` is your local branch
## Push vs Pull vs Fetch
### Git Push
Uploads your local commits to the remote:
```bash
git push origin main
```
Use when: You have local commits ready to share with the team
### Git Pull
Downloads and merges remote changes:
```bash
git pull origin main
```
Use when: You want to update your branch with remote changes
Equivalent to: `git fetch origin` + `git merge origin/main`
### Git Fetch
Downloads remote changes without merging:
```bash
git fetch origin
```
Use when: You want to see what's changed before merging
Safer than pull because it doesn't automatically merge
## Common Remote Workflows
### Daily Work Flow
```bash
# Start of day: get latest changes
git checkout main
git pull origin main
# Create feature branch
git checkout -b my-feature
# Do work, make commits
git add .
git commit -m "Add feature"
# Before pushing, update with latest main
git checkout main
git pull origin main
git checkout my-feature
git merge main
# Push your feature
git push -u origin my-feature
```
### Collaboration Workflow
```bash
# Teammate pushed changes to main
git fetch origin
git log origin/main # Review changes
git merge origin/main # Merge into current branch
# Or use pull (fetch + merge in one step)
git pull origin main
```
### Syncing Fork (with upstream)
```bash
# Add original repo as upstream
git remote add upstream <original-repo-url>
# Get latest from upstream
git fetch upstream
git checkout main
git merge upstream/main
# Push to your fork
git push origin main
```
## Handling Push Rejection
If push is rejected because remote has changes:
```bash
# Remote has commits you don't have
git push origin main
# Error: Updates were rejected
# Solution 1: Pull first (creates merge commit)
git pull origin main
git push origin main
# Solution 2: Pull with rebase (cleaner history)
git pull --rebase origin main
git push origin main
```
## What You'll Learn
Working with remotes is fundamental to team collaboration. Understanding the difference between local and remote branches, knowing when to push/pull/fetch, and managing synchronization are core skills for any developer. While this module uses a local "remote" for learning, the concepts apply directly to GitHub, GitLab, and other hosting services. Mastering remotes enables you to work effectively in distributed teams and contribute to open source projects.

View File

@@ -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