Compare commits
18 Commits
f11f5a4646
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0a9f7c1842 | ||
|
|
7f34cf2d08 | ||
|
|
eb63970b7a | ||
|
|
dff82c847c | ||
|
|
c057fd617b | ||
|
|
f48eefee10 | ||
|
|
df9a2bf7c1 | ||
|
|
7fb84560f5 | ||
|
|
6b0e84934a | ||
|
|
30b878fc67 | ||
|
|
aefcfbe100 | ||
|
|
5f8091d5b2 | ||
|
|
cf073d569e | ||
|
|
d7c146975d | ||
|
|
dc94520b2a | ||
|
|
e29b9bca70 | ||
|
|
a7b511c8cb | ||
|
|
a8eb66d3c9 |
@@ -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:
|
||||
225
01_essentials/02-history/README.md
Normal file
225
01_essentials/02-history/README.md
Normal 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.
|
||||
@@ -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
|
||||
@@ -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 {
|
||||
@@ -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)
|
||||
```
|
||||
|
||||
@@ -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 ""
|
||||
@@ -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
|
||||
229
01_essentials/04-merging/README.md
Normal file
229
01_essentials/04-merging/README.md
Normal 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! 🎉
|
||||
246
01_essentials/04-merging/setup.ps1
Normal file
246
01_essentials/04-merging/setup.ps1
Normal 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 ""
|
||||
122
01_essentials/04-merging/verify.ps1
Normal file
122
01_essentials/04-merging/verify.ps1
Normal 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
|
||||
}
|
||||
481
01_essentials/05-merge-conflicts/README.md
Normal file
481
01_essentials/05-merge-conflicts/README.md
Normal 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!
|
||||
@@ -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 ..
|
||||
@@ -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 ..
|
||||
@@ -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>
|
||||
```
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
904
01_essentials/09-multiplayer/FACILITATOR-SETUP.md
Normal file
904
01_essentials/09-multiplayer/FACILITATOR-SETUP.md
Normal 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.**
|
||||
1408
01_essentials/09-multiplayer/README.md
Normal file
1408
01_essentials/09-multiplayer/README.md
Normal file
File diff suppressed because it is too large
Load Diff
@@ -86,7 +86,7 @@ git rebase --abort
|
||||
git branch
|
||||
|
||||
# Switch to a branch
|
||||
git checkout <branch-name>
|
||||
git switch <branch-name>
|
||||
```
|
||||
|
||||
## Verification
|
||||
169
02_advanced/05-blame/README.md
Normal file
169
02_advanced/05-blame/README.md
Normal 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.
|
||||
24
02_advanced/05-blame/reset.ps1
Normal file
24
02_advanced/05-blame/reset.ps1
Normal 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"
|
||||
323
02_advanced/05-blame/setup.ps1
Normal file
323
02_advanced/05-blame/setup.ps1
Normal 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 ""
|
||||
114
02_advanced/05-blame/verify.ps1
Normal file
114
02_advanced/05-blame/verify.ps1
Normal 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
|
||||
}
|
||||
448
02_advanced/06-merge-strategies/README.md
Normal file
448
02_advanced/06-merge-strategies/README.md
Normal 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!
|
||||
24
02_advanced/06-merge-strategies/reset.ps1
Normal file
24
02_advanced/06-merge-strategies/reset.ps1
Normal 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"
|
||||
221
02_advanced/06-merge-strategies/setup.ps1
Normal file
221
02_advanced/06-merge-strategies/setup.ps1
Normal 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 ""
|
||||
140
02_advanced/06-merge-strategies/verify.ps1
Normal file
140
02_advanced/06-merge-strategies/verify.ps1
Normal 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
493
GIT-CHEATSHEET.md
Normal 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
442
README.md
@@ -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! 🚀
|
||||
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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 ""
|
||||
@@ -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
|
||||
}
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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"
|
||||
@@ -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
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user