Compare commits
2 Commits
6b0e84934a
...
df9a2bf7c1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
df9a2bf7c1 | ||
|
|
7fb84560f5 |
@@ -43,12 +43,12 @@ Your goal is to create a feature branch, make commits on it, and understand how
|
||||
5. Stage and commit: `git add login.py` and `git commit -m "Add login functionality"`
|
||||
6. Modify `login.py`, then commit again
|
||||
7. Switch back to main: `git switch main`
|
||||
8. Run `ls` and notice that `login.py` doesn't exist on main!
|
||||
8. Run `ls` (or check your file explorer) and notice that `login.py` doesn't exist on main!
|
||||
9. Switch back to feature-login: `git switch feature-login`
|
||||
10. Run `ls` again and see that `login.py` is back!
|
||||
10. Run `ls` (or check your file explorer) again and see that `login.py` is back!
|
||||
|
||||
> **Important Notes:**
|
||||
> - Use `git switch` to change branches (modern Git command)
|
||||
> - Use `git switch` to change branches
|
||||
> - `git switch -c <name>` creates and switches in one command
|
||||
> - 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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
# Module 04: Merging
|
||||
# Module 04: Merging Branches
|
||||
|
||||
## 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
|
||||
- Merge divergent branches together
|
||||
- Understand merge commits and their purpose
|
||||
- Use `git merge` to combine branches
|
||||
- Visualize merge history with `git log --graph`
|
||||
|
||||
## Challenge
|
||||
|
||||
@@ -19,72 +19,217 @@ Run the setup script to create your challenge environment:
|
||||
.\setup.ps1
|
||||
```
|
||||
|
||||
This will create a `challenge/` directory with a Git repository that has a main branch and a feature branch ready to merge.
|
||||
This will create a `challenge/` directory with a Git repository that has two divergent branches ready to merge.
|
||||
|
||||
### Your Task
|
||||
|
||||
This challenge has two parts that teach you about the two types of merges in Git:
|
||||
You have two branches that have diverged - both `main` and `feature-login` have new commits since they split. Your task is to merge them back together.
|
||||
|
||||
**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)
|
||||
**Scenario:**
|
||||
- You're working on a team project
|
||||
- You created a `feature-login` branch to add login functionality
|
||||
- While you were working, your teammate added documentation to `main`
|
||||
- Now you need to merge your login feature back into main
|
||||
|
||||
**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:**
|
||||
**Steps:**
|
||||
|
||||
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 switch -c feature-ui`
|
||||
7. Create a new file `ui.py` and commit it
|
||||
8. Make another commit on feature-ui (modify ui.py)
|
||||
9. Switch back to main: `git switch main`
|
||||
10. Make a change on main (modify api.py) and commit it
|
||||
11. Merge feature-ui: `git merge feature-ui`
|
||||
12. View the merge history: `git log --oneline --graph --all`
|
||||
3. View the branch structure: `git log --oneline --graph --all`
|
||||
4. Merge the feature-login branch: `git merge feature-login`
|
||||
5. View the merge result: `git log --oneline --graph --all`
|
||||
6. Examine the merge commit: `git show HEAD`
|
||||
|
||||
> **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`
|
||||
> - When both branches have new commits, Git creates a **merge commit**
|
||||
> - The merge commit has TWO parent commits (one from each branch)
|
||||
> - Git will open an editor for you to write a merge commit message
|
||||
> - You can use the default message or customize it
|
||||
> - The `--graph` option helps visualize how branches merged
|
||||
|
||||
## 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.
|
||||
### What is Merging?
|
||||
|
||||
**Merging** combines changes from different branches into one branch. When you merge, you're telling Git: "Take all the changes from branch X and incorporate them into my current branch."
|
||||
|
||||
### Divergent Branches
|
||||
|
||||
When two branches have different commits since they split, they are **divergent**:
|
||||
|
||||
```
|
||||
main: A---B---C
|
||||
\
|
||||
feature: D---E
|
||||
```
|
||||
|
||||
- Both branches split from commit B
|
||||
- `main` has commit C
|
||||
- `feature` has commits D and E
|
||||
- They have **diverged** - each has unique work
|
||||
|
||||
### The Merge Commit
|
||||
|
||||
When you merge divergent branches, Git creates a special **merge commit**:
|
||||
|
||||
```
|
||||
Before merge:
|
||||
main: A---B---C
|
||||
\
|
||||
feature: D---E
|
||||
|
||||
After merge (on main):
|
||||
main: A---B---C-------M
|
||||
\ /
|
||||
feature: D---E---/
|
||||
```
|
||||
|
||||
**Merge commit (M)** is special because:
|
||||
- It has **two parent commits**: C and E
|
||||
- It represents the point where branches came back together
|
||||
- It contains the combined changes from both branches
|
||||
|
||||
### How Git Merges
|
||||
|
||||
Git uses a **three-way merge** algorithm:
|
||||
|
||||
1. **Common ancestor** (B) - Where the branches split
|
||||
2. **Your branch** (C) - Latest commit on main
|
||||
3. **Their branch** (E) - Latest commit on feature
|
||||
|
||||
Git compares all three to figure out what changed on each branch and combines them intelligently.
|
||||
|
||||
## Understanding Merge Output
|
||||
|
||||
When you run `git merge feature-login`, you'll see:
|
||||
|
||||
```
|
||||
Merge made by the 'ort' strategy.
|
||||
login.py | 25 +++++++++++++++++++++++++
|
||||
1 file changed, 25 insertions(+)
|
||||
create mode 100644 login.py
|
||||
```
|
||||
|
||||
**What this means:**
|
||||
- **"Merge made by the 'ort' strategy"** - Git used its merge algorithm to combine changes
|
||||
- The summary shows what files changed in the merge
|
||||
- A new merge commit was created
|
||||
|
||||
### The Merge Commit Message
|
||||
|
||||
Git will open your editor with a default message:
|
||||
|
||||
```
|
||||
Merge branch 'feature-login'
|
||||
|
||||
# Please enter a commit message to explain why this merge is necessary,
|
||||
# especially if it merges an updated upstream into a topic branch.
|
||||
```
|
||||
|
||||
**Tips for merge messages:**
|
||||
- The default message is usually fine for simple merges
|
||||
- For important merges, explain WHY you're merging
|
||||
- Example: "Merge feature-login - adds user authentication system"
|
||||
|
||||
## Visualizing Merges
|
||||
|
||||
Use `git log --graph` to see the branch structure:
|
||||
|
||||
```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 continuing
|
||||
- `/` and `\` = Branch splitting or merging
|
||||
- `(HEAD -> main)` = Current branch position
|
||||
- The merge commit has two lines coming into it
|
||||
|
||||
## Useful Commands
|
||||
|
||||
### Merging
|
||||
|
||||
```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)
|
||||
git merge <branch> # Merge branch into current branch
|
||||
git merge --abort # Cancel a merge in progress
|
||||
```
|
||||
|
||||
### Viewing Merge History
|
||||
|
||||
```bash
|
||||
git log --oneline --graph # Simple graph view
|
||||
git log --oneline --graph --all # Show all branches
|
||||
git log --merges # Show only merge commits
|
||||
git show <commit> # View details of a merge commit
|
||||
```
|
||||
|
||||
### Branch Management
|
||||
|
||||
```bash
|
||||
git branch # List local branches
|
||||
git branch -a # List all branches (including remote)
|
||||
git branch -d <branch> # Delete a merged branch
|
||||
```
|
||||
|
||||
## Common Questions
|
||||
|
||||
### "What if the merge fails?"
|
||||
|
||||
Sometimes Git can't automatically merge changes (this is called a **merge conflict**). Don't worry! We'll cover this in the next module (Module 05: Merge Conflicts).
|
||||
|
||||
If you see a conflict and want to cancel:
|
||||
```bash
|
||||
git merge --abort
|
||||
```
|
||||
|
||||
### "Can I undo a merge?"
|
||||
|
||||
Before you've pushed to remote:
|
||||
```bash
|
||||
git reset --hard HEAD~1 # Undo the merge commit
|
||||
```
|
||||
|
||||
We'll cover more advanced undoing in later modules.
|
||||
|
||||
### "Should I delete the feature branch after merging?"
|
||||
|
||||
It's common practice to delete feature branches after merging:
|
||||
|
||||
```bash
|
||||
git branch -d feature-login # Safe delete (only works if merged)
|
||||
```
|
||||
|
||||
This cleans up your branch list. The commits are still in the history - the branch pointer is just removed.
|
||||
|
||||
### "What does 'ort strategy' mean?"
|
||||
|
||||
Git uses different merge algorithms:
|
||||
- **ort** (default in Git 2.34+) - Newer, faster merge algorithm
|
||||
- **recursive** (older) - Traditional merge algorithm
|
||||
|
||||
You don't need to worry about this - Git picks the best one automatically!
|
||||
|
||||
## Verification
|
||||
|
||||
Once you've completed both merges, verify your solution:
|
||||
Once you've completed the merge, 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.
|
||||
The verification script will check that you've successfully merged the feature branch.
|
||||
|
||||
## Need to Start Over?
|
||||
|
||||
@@ -95,3 +240,9 @@ If you want to reset the challenge and start fresh:
|
||||
```
|
||||
|
||||
This will remove the challenge directory and run the setup script again, giving you a clean slate.
|
||||
|
||||
## What's Next?
|
||||
|
||||
Now that you understand basic merging, the next module covers **Merge Conflicts** - what happens when Git can't automatically merge changes and how to resolve them manually.
|
||||
|
||||
**Advanced Topic:** In Module 06 (Advanced section), you'll learn about different merge strategies including fast-forward merges and when to use `--no-ff`.
|
||||
|
||||
@@ -5,8 +5,7 @@
|
||||
|
||||
.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.
|
||||
contains two divergent branches ready to merge (three-way merge scenario).
|
||||
#>
|
||||
|
||||
Write-Host "`n=== Setting up Module 04 Challenge ===" -ForegroundColor Cyan
|
||||
@@ -30,99 +29,121 @@ git init | Out-Null
|
||||
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
|
||||
# Commit 1: Initial project structure on main
|
||||
Write-Host "Creating initial project structure..." -ForegroundColor Green
|
||||
$readmeContent = @"
|
||||
# My Project
|
||||
|
||||
def api_handler():
|
||||
print("API Handler initialized")
|
||||
return True
|
||||
A simple web application project.
|
||||
|
||||
## Setup
|
||||
|
||||
Coming soon...
|
||||
"@
|
||||
Set-Content -Path "api.py" -Value $apiContent
|
||||
Set-Content -Path "README.md" -Value $readmeContent
|
||||
|
||||
$appContent = @"
|
||||
# app.py - Main application file
|
||||
|
||||
def main():
|
||||
print("Welcome to My App!")
|
||||
pass
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
"@
|
||||
Set-Content -Path "app.py" -Value $appContent
|
||||
|
||||
git add .
|
||||
git commit -m "Initial API setup" | Out-Null
|
||||
git commit -m "Initial project structure" | Out-Null
|
||||
|
||||
# Commit 2: Add more API functionality on main
|
||||
$apiContent = @"
|
||||
# api.py - API module
|
||||
# Create feature-login branch
|
||||
Write-Host "Creating feature-login branch..." -ForegroundColor Green
|
||||
git switch -c feature-login | Out-Null
|
||||
|
||||
def api_handler():
|
||||
print("API Handler initialized")
|
||||
# Commit on feature-login: Add login module
|
||||
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 get_data():
|
||||
print("Fetching data from API...")
|
||||
return {"status": "ok"}
|
||||
def logout(username):
|
||||
"""Log out a user."""
|
||||
print(f"Logging out user: {username}")
|
||||
return True
|
||||
"@
|
||||
Set-Content -Path "api.py" -Value $apiContent
|
||||
Set-Content -Path "login.py" -Value $loginContent
|
||||
|
||||
git add .
|
||||
git commit -m "Add get_data function" | Out-Null
|
||||
git commit -m "Add login module" | 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
|
||||
# Second commit on feature-login: Integrate login with app
|
||||
$appContent = @"
|
||||
# app.py - Main application file
|
||||
from login import login, logout
|
||||
|
||||
# Commit on feature-api: Add API routes
|
||||
$routesContent = @"
|
||||
# api-routes.py - API Routes module
|
||||
def main():
|
||||
print("Welcome to My App!")
|
||||
# Add login integration
|
||||
if login("testuser", "password"):
|
||||
print("Login successful!")
|
||||
pass
|
||||
|
||||
def setup_routes():
|
||||
print("Setting up API routes...")
|
||||
routes = {
|
||||
"/api/data": "get_data",
|
||||
"/api/status": "get_status"
|
||||
}
|
||||
return routes
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
"@
|
||||
Set-Content -Path "api-routes.py" -Value $routesContent
|
||||
Set-Content -Path "app.py" -Value $appContent
|
||||
|
||||
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
|
||||
git commit -m "Integrate login with main app" | Out-Null
|
||||
|
||||
# Switch back to main branch
|
||||
Write-Host "Switching back to main branch..." -ForegroundColor Green
|
||||
git checkout main 2>$null | Out-Null
|
||||
git switch main | Out-Null
|
||||
|
||||
# Make a commit 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
|
||||
|
||||
# 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 "`nScenario: You have two divergent branches!" -ForegroundColor Yellow
|
||||
Write-Host " - main: Has updated documentation" -ForegroundColor White
|
||||
Write-Host " - feature-login: Has new login functionality" -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 " 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 ""
|
||||
|
||||
@@ -8,9 +8,9 @@
|
||||
- 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)
|
||||
- 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
|
||||
@@ -38,82 +38,63 @@ 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
|
||||
Write-Host "[HINT] Switch to main with: git switch 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
|
||||
# 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-api' not found (may have been deleted after merge)" -ForegroundColor Cyan
|
||||
Write-Host "[INFO] Branch 'feature-login' not found (may have been deleted after merge)" -ForegroundColor Cyan
|
||||
}
|
||||
|
||||
# Check if commits from feature-api are in main
|
||||
$apiRoutesCommit = git log --all --grep="Add API routes" --oneline 2>$null
|
||||
$healthCheckCommit = git log --all --grep="Add health check route" --oneline 2>$null
|
||||
# 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
|
||||
}
|
||||
|
||||
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
|
||||
# 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] Commits from feature-api not found in main" -ForegroundColor Red
|
||||
Write-Host "[HINT] Merge feature-api with: git merge feature-api" -ForegroundColor Yellow
|
||||
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] Expected commits from feature-api not found" -ForegroundColor Red
|
||||
Write-Host "[HINT] Did you run setup.ps1?" -ForegroundColor Yellow
|
||||
Write-Host "[FAIL] app.py not found" -ForegroundColor Red
|
||||
$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)
|
||||
# Check for merge commit (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
|
||||
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 commits found" -ForegroundColor Red
|
||||
Write-Host "[HINT] Create divergence: make commits on both main and feature-ui, then merge" -ForegroundColor Yellow
|
||||
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 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
|
||||
# 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 "[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
|
||||
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 ..
|
||||
@@ -124,13 +105,14 @@ if ($allChecksPassed) {
|
||||
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 "`nYou've successfully completed your first branch merge!" -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 " - 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
|
||||
|
||||
448
03_advanced/06-merge-strategies/README.md
Normal file
448
03_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
03_advanced/06-merge-strategies/reset.ps1
Normal file
24
03_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
03_advanced/06-merge-strategies/setup.ps1
Normal file
221
03_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
03_advanced/06-merge-strategies/verify.ps1
Normal file
140
03_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
|
||||
}
|
||||
Reference in New Issue
Block a user