From df9a2bf7c185ff9e52448f159c9e2b0c7f9fe24c Mon Sep 17 00:00:00 2001 From: Bjarke Sporring Date: Wed, 7 Jan 2026 21:50:59 +0100 Subject: [PATCH] refactor: split out merge strategies from essentials --- 01_essentials/04-merging/README.md | 239 +++++++++-- 01_essentials/04-merging/setup.ps1 | 159 ++++---- 01_essentials/04-merging/verify.ps1 | 110 +++-- 03_advanced/06-merge-strategies/README.md | 448 +++++++++++++++++++++ 03_advanced/06-merge-strategies/reset.ps1 | 24 ++ 03_advanced/06-merge-strategies/setup.ps1 | 221 ++++++++++ 03_advanced/06-merge-strategies/verify.ps1 | 140 +++++++ 7 files changed, 1164 insertions(+), 177 deletions(-) create mode 100644 03_advanced/06-merge-strategies/README.md create mode 100644 03_advanced/06-merge-strategies/reset.ps1 create mode 100644 03_advanced/06-merge-strategies/setup.ps1 create mode 100644 03_advanced/06-merge-strategies/verify.ps1 diff --git a/01_essentials/04-merging/README.md b/01_essentials/04-merging/README.md index ac93c86..cf151b5 100644 --- a/01_essentials/04-merging/README.md +++ b/01_essentials/04-merging/README.md @@ -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 # 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 # Delete a merged branch (optional) +git merge # 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 # 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 # 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`. diff --git a/01_essentials/04-merging/setup.ps1 b/01_essentials/04-merging/setup.ps1 index 230fc29..a653db4 100644 --- a/01_essentials/04-merging/setup.ps1 +++ b/01_essentials/04-merging/setup.ps1 @@ -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 "" diff --git a/01_essentials/04-merging/verify.ps1 b/01_essentials/04-merging/verify.ps1 index e01da32..6c3f9cd 100644 --- a/01_essentials/04-merging/verify.ps1 +++ b/01_essentials/04-merging/verify.ps1 @@ -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 diff --git a/03_advanced/06-merge-strategies/README.md b/03_advanced/06-merge-strategies/README.md new file mode 100644 index 0000000..eed5a26 --- /dev/null +++ b/03_advanced/06-merge-strategies/README.md @@ -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 # Let Git decide automatically +git merge --no-ff # Force a merge commit +git merge --ff-only # 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! diff --git a/03_advanced/06-merge-strategies/reset.ps1 b/03_advanced/06-merge-strategies/reset.ps1 new file mode 100644 index 0000000..ed0ffa9 --- /dev/null +++ b/03_advanced/06-merge-strategies/reset.ps1 @@ -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" diff --git a/03_advanced/06-merge-strategies/setup.ps1 b/03_advanced/06-merge-strategies/setup.ps1 new file mode 100644 index 0000000..2eca7f9 --- /dev/null +++ b/03_advanced/06-merge-strategies/setup.ps1 @@ -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 "" diff --git a/03_advanced/06-merge-strategies/verify.ps1 b/03_advanced/06-merge-strategies/verify.ps1 new file mode 100644 index 0000000..222c251 --- /dev/null +++ b/03_advanced/06-merge-strategies/verify.ps1 @@ -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 +}