diff --git a/01-essentials/03-branching-and-merging/README.md b/01-essentials/03-branching-and-merging/README.md index 55c028b..f40ef70 100644 --- a/01-essentials/03-branching-and-merging/README.md +++ b/01-essentials/03-branching-and-merging/README.md @@ -1,22 +1,16 @@ # Module 03: Branching and Merging -## About This Module +## Learning Objectives -Welcome to Module 03! This module is different from the others - it uses a **checkpoint system** that lets you work through three related concepts in one continuous repository: +By the end of this module, you will: +- Understand what branches are and why they're useful +- Create and switch between branches +- Make commits on different branches +- Merge branches together +- Visualize branch history with `git log --graph` +- Understand merge commits and how they work -1. **Branching Basics** - Create and work with feature branches -2. **Merging Branches** - Combine branches together -3. **Resolving Merge Conflicts** - Fix conflicts when Git can't merge automatically - -Instead of three separate modules, you'll progress through checkpoints in a single Git repository, building on each previous section. You can jump between checkpoints, skip ahead, or restart any section at any time! - -### Why Checkpoints? - -Branching, merging, and conflict resolution are naturally connected - you can't understand merging without branches, and you can't master conflicts without trying to merge. The checkpoint system lets you learn these concepts as a continuous workflow, just like real development. - -## Quick Start - -### Setup +## Setup Create the challenge environment: @@ -24,816 +18,403 @@ Create the challenge environment: .\setup.ps1 ``` -This creates a complete Git repository with all checkpoints ready. +This creates a repository with a realistic project history showing multiple merged feature branches. -### Working with Checkpoints +## Overview -**View available checkpoints:** -```bash -.\reset.ps1 -``` +**Branching** lets you create independent lines of development. Think of it like parallel universes for your code - you can experiment on one branch without affecting others. -**Jump to a specific checkpoint:** -```bash -.\reset.ps1 start # Checkpoint 1: Branching Basics -.\reset.ps1 merge # Checkpoint 2: Merging Branches -.\reset.ps1 merge-conflict # Checkpoint 3: Resolving Conflicts -``` - -**Verify your progress:** -```bash -.\verify.ps1 # Verify all checkpoints complete -.\verify.ps1 start # Verify Checkpoint 1 only -.\verify.ps1 merge # Verify Checkpoint 2 only -.\verify.ps1 merge-conflict # Verify Checkpoint 3 only -``` - -### Recommended Workflow - -Complete checkpoints in order: -1. Start with Checkpoint 1 (Branching Basics) -2. Progress to Checkpoint 2 (Merging) -3. Finish with Checkpoint 3 (Merge Conflicts) - -Or skip to any checkpoint if you already know the earlier concepts! - ---- - -## Checkpoint 1: Branching Basics - -### Learning Objectives - -- Understand what a branch is in Git -- Create new branches with `git switch -c` -- Switch between branches with `git switch` -- View all branches with `git branch` -- Understand that branches are independent lines of development - -### Your Task - -Create a feature branch called `feature-login`, add a `login.py` file, and make commits to demonstrate that branches allow independent development. - -**Steps:** - -1. Navigate to the challenge directory: `cd challenge` -2. Create a new branch: `git switch -c feature-login` -3. Create a file: `login.py` (with any content you like) -4. Commit your file: `git add login.py && git commit -m "Add login module"` -5. Make another change to `login.py` and commit it -6. Switch back to main: `git switch main` -7. Notice that `login.py` doesn't exist on main! -8. Switch back to your feature: `git switch feature-login` -9. Notice that `login.py` exists again! - -**Verify:** Run `.\verify.ps1 start` to check your solution. - -### What is a Branch? - -A **branch** in Git is an independent line of development. Think of it as a parallel universe for your code - you can make changes without affecting the main timeline. - -**Visual representation:** - -``` -main: A---B---C - \ -feature-login: D---E -``` - -- Both branches share commits A and B -- Branch `main` continues with commit C -- Branch `feature-login` goes in a different direction with commits D and E -- Changes in one branch don't affect the other! +**Merging** combines work from different branches back together. ### Why Use Branches? -Branches let you: -- **Experiment safely** - Try new ideas without breaking main +- **Experiment safely** - Try new ideas without breaking main code - **Work in parallel** - Multiple features can be developed simultaneously - **Organize work** - Each feature/fix gets its own branch - **Collaborate better** - Team members work on separate branches -### Key Concepts +## Your Task -- **Branch**: A lightweight movable pointer to a commit -- **HEAD**: A pointer showing which branch you're currently on -- **main**: The default branch (formerly called "master") -- **Feature branch**: A branch created for a specific feature or task +### Part 1: Explore the Repository -### Useful Commands - -```bash -# View all branches (current branch marked with *) -git branch - -# Create a new branch -git branch feature-login - -# Switch to a branch -git switch feature-login - -# Create AND switch in one command -git switch -c feature-login - -# Switch back to previous branch -git switch - - -# Delete a branch (only if merged) -git branch -d feature-login - -# Force delete a branch -git branch -D feature-login -``` - -### Understanding HEAD - -`HEAD` is Git's way of saying "you are here." It points to your current branch. - -When you run `git switch main`, HEAD moves to point to main. -When you run `git switch feature-login`, HEAD moves to point to feature-login. - ---- - -## Checkpoint 2: Merging Branches - -**Prerequisites:** Complete Checkpoint 1 OR run `.\reset.ps1 merge` - -### Learning Objectives - -- Understand what merging means in Git -- Merge a feature branch back into main -- Use `git merge` to combine branches -- Understand merge commits -- Visualize merged branches with `git log --graph` - -### Your Task - -You've completed work on your `feature-login` branch. Now merge it back into `main` to include the login functionality in your main codebase. - -**Scenario:** -- You created the `feature-login` branch and added login functionality -- Meanwhile, development continued on `main` (README and app.py were added) -- Now you need to merge your login feature into main - -**Steps:** - -1. Make sure you're in the challenge directory: `cd challenge` -2. Check which branch you're on: `git branch` -3. Switch to main if needed: `git switch main` -4. View the branch structure: `git log --oneline --graph --all` -5. Merge feature-login into main: `git merge feature-login` -6. View the result: `git log --oneline --graph --all` - -**Verify:** Run `.\verify.ps1 merge` to check your solution. - -### What is Merging? - -**Merging** is the process of combining changes from one branch into another. - -Think of it like combining two streams into one river - all the water (code) flows together. - -#### Before Merging - -You have two branches with different work: - -``` -main: A---B---C---D - \ -feature-login: E---F -``` - -- Main branch progressed with commits C and D -- Feature-login branch has commits E and F -- They diverged at commit B - -#### After Merging - -You bring the feature branch into main: - -``` -main: A---B---C---D---M - \ / -feature-login: E-----F -``` - -- Commit M is a **merge commit** - it combines both branches -- Main now has all the work from both branches -- Your login feature is now part of main! - -### How to Merge - -Merging is simple - just two steps: - -**1. Switch to the branch you want to merge INTO:** -```bash -git switch main -``` -This is the branch that will receive the changes. - -**2. Merge the other branch:** -```bash -git merge feature-login -``` -This brings changes from `feature-login` into `main`. - -**That's it!** Git automatically combines the changes. - -### Understanding Merge Commits - -When you merge, Git creates a special commit called a **merge commit**. - -**What makes it special?** -- It has TWO parent commits (one from each branch) -- It represents the point where branches come back together -- The message typically says "Merge branch 'feature-login'" - -**See your merge commit:** -```bash -git log --oneline -``` - -Look for the merge commit at the top - it will say something like: -``` -abc1234 Merge branch 'feature-login' -``` - -### Types of Merges - -**Three-way merge** (what you just did): -- Both branches have new commits -- Git creates a merge commit -- History shows both branches clearly - -**Fast-forward merge**: -- Main hasn't changed since the branch was created -- Git just moves the main pointer forward -- No merge commit needed! - -``` -# Before (fast-forward merge) -main: A---B - \ -feature: C---D - -# After (main just moves forward) -main: A---B---C---D -``` - -### Visualizing Branches - -The `--graph` flag is your best friend: - -```bash -git log --oneline --graph --all -``` - -**What the graph shows:** -- `*` = A commit -- `|` = A branch line -- `/` and `\` = Branches splitting/joining -- Branch names in parentheses - -**Example output:** -``` -* a1b2c3d (HEAD -> main) Merge branch 'feature-login' -|\ -| * e4f5g6h (feature-login) Add password validation -| * i7j8k9l Add login module -* | m1n2o3p Add README documentation -* | q4r5s6t Add app.py entry point -|/ -* u7v8w9x Add main functionality -* y1z2a3b Initial commit -``` - -### Useful Commands - -```bash -# Merge a branch into your current branch -git merge - -# Abort a merge if something goes wrong -git merge --abort - -# View merge commits only -git log --merges - -# View branch structure -git log --oneline --graph --all - -# See which branches have been merged into main -git branch --merged main - -# See which branches haven't been merged -git branch --no-merged main -``` - ---- - -## Checkpoint 3: Resolving Merge Conflicts - -**Prerequisites:** Complete Checkpoint 2 OR run `.\reset.ps1 merge-conflict` - -### Learning Objectives - -- Understand what merge conflicts are and why they occur -- Identify merge conflicts in your repository -- Read and interpret conflict markers (`<<<<<<<`, `=======`, `>>>>>>>`) -- Resolve merge conflicts manually -- Complete a merge after resolving conflicts - -### Your Task - -You have an `update-config` branch that modified `config.json`, and the main branch also modified `config.json` in a different way. When you try to merge, Git can't automatically combine them - you'll need to resolve the conflict manually. - -**Your mission:** -1. Attempt to merge the `update-config` branch into `main` -2. Git will tell you there's a conflict - don't panic! -3. Resolve the conflict by keeping BOTH settings (timeout AND debug) -4. Complete the merge - -**Steps:** - -1. Make sure you're in challenge directory: `cd challenge` -2. Verify you're on main: `git branch` -3. Try to merge: `git merge update-config` -4. Git will report a conflict! -5. Open `config.json` in your text editor -6. Follow the resolution guide below -7. Save the file -8. Stage the resolved file: `git add config.json` -9. Complete the merge: `git commit` - -**Verify:** Run `.\verify.ps1 merge-conflict` to check your solution. - -### What Are Merge Conflicts? - -A **merge conflict** occurs when Git cannot automatically combine changes because both branches modified the same part of the same file. - -**Example scenario:** -``` -main branch: changes line 5 to: "timeout": 5000 -update-config: changes line 5 to: "debug": true -``` - -Git doesn't know which one you want (or if you want both)! So it asks you to decide. - -**When do conflicts happen?** -- ✅ Two branches modify the same lines in a file -- ✅ One branch deletes a file that another branch modifies -- ✅ Complex changes Git can't merge automatically -- ❌ Different files are changed (no conflict!) -- ❌ Different parts of the same file are changed (no conflict!) - -**Don't fear conflicts!** They're a normal part of collaborative development. Git just needs your help to decide what the final code should look like. - -### Step-by-Step: Resolving Your First Conflict - -#### Step 1: Attempt the Merge +First, explore the existing history to see what merging looks like: ```bash cd challenge -git merge update-config +git log --oneline --graph --all ``` -**You'll see:** -``` -Auto-merging config.json -CONFLICT (content): Merge conflict in config.json -Automatic merge failed; fix conflicts and then commit the result. +You'll see a visual graph showing: +- The `main` branch timeline +- Feature branches that split off from main +- Merge points where branches come back together + +**Study the graph:** +- Look for the `*` symbols (commits) +- Notice the `|`, `/`, and `\` characters (branch lines) +- Find the merge commits (they have two parent lines converging) + +**Explore the branches:** +```bash +# See all branches +git branch --all + +# Check which files exist on different branches +git ls-tree --name-only feature-login +git ls-tree --name-only feature-api +git ls-tree --name-only main ``` -**Don't panic!** This is normal. Git is just asking for your help. +**View specific merges:** +```bash +# See all merge commits +git log --merges --oneline -#### Step 2: Check What Happened +# See details of a specific merge +git show +``` + +### Part 2: Create Your Own Branch + +Now practice creating your own branch: ```bash -git status +# Create and switch to a new branch +git switch -c my-feature + +# Verify you're on the new branch +git branch ``` -**You'll see:** -``` -On branch main -You have unmerged paths. - (fix conflicts and run "git commit") - (use "git merge --abort" to abort the merge) +The `*` shows which branch you're currently on. -Unmerged paths: - (use "git add ..." to mark resolution) - both modified: config.json -``` +### Part 3: Make Commits on Your Branch -This tells you that `config.json` needs your attention! - -#### Step 3: Open the Conflicted File - -Open `config.json` in your text editor. You'll see special **conflict markers**: - -```json -{ - "app": { - "name": "MyApp", - "version": "1.0.0", - "port": 3000, -<<<<<<< HEAD - "timeout": 5000 -======= - "debug": true ->>>>>>> update-config - } -} -``` - -#### Step 4: Understand the Conflict Markers - -``` -<<<<<<< HEAD - "timeout": 5000 ← Your current branch (main) -======= - "debug": true ← The branch you're merging (update-config) ->>>>>>> update-config -``` - -**What each marker means:** -- `<<<<<<< HEAD` - Start of your changes (current branch) -- `=======` - Separator between the two versions -- `>>>>>>> update-config` - End of their changes (branch being merged) - -#### Step 5: Decide What to Keep - -You have three options: - -**Option 1: Keep ONLY your changes (timeout)** -```json - "timeout": 5000 -``` - -**Option 2: Keep ONLY their changes (debug)** -```json - "debug": true -``` - -**Option 3: Keep BOTH changes** ← This is what we want! -```json - "timeout": 5000, - "debug": true -``` - -For this challenge, choose **Option 3** - keep both settings! - -#### Step 6: Edit the File - -Delete ALL the conflict markers and keep both settings: - -**Before (with conflict markers):** -```json -{ - "app": { - "name": "MyApp", - "version": "1.0.0", - "port": 3000, -<<<<<<< HEAD - "timeout": 5000 -======= - "debug": true ->>>>>>> update-config - } -} -``` - -**After (resolved):** -```json -{ - "app": { - "name": "MyApp", - "version": "1.0.0", - "port": 3000, - "timeout": 5000, - "debug": true - } -} -``` - -**Important:** -- Remove `<<<<<<< HEAD` -- Remove `=======` -- Remove `>>>>>>> update-config` -- Keep both the timeout and debug settings -- Ensure valid JSON syntax (notice the comma after timeout!) - -#### Step 7: Save the File - -Save `config.json` with your changes. - -#### Step 8: Stage the Resolved File - -Tell Git you've resolved the conflict: +Add some changes to your branch: ```bash -git add config.json +# Create a new file or modify an existing one +echo "# My Feature" > my-feature.md + +# Stage and commit +git add . +git commit -m "Add my feature" + +# Make another commit +echo "More details" >> my-feature.md +git add . +git commit -m "Expand feature documentation" ``` -#### Step 9: Check Status +**Important:** Changes on your branch don't affect main! ```bash -git status +# Switch to main +git switch main + +# Notice your my-feature.md doesn't exist here +ls + +# Switch back to your branch +git switch my-feature + +# Now it exists again! +ls ``` -**You'll see:** -``` -On branch main -All conflicts fixed but you are still merging. - (use "git commit" to conclude merge) -``` +### Part 4: Merge Your Branch -Perfect! Git confirms the conflict is resolved. - -#### Step 10: Complete the Merge - -Commit the merge: +Bring your work into main: ```bash -git commit +# Switch to the branch you want to merge INTO +git switch main + +# Merge your feature branch +git merge my-feature + +# View the result +git log --oneline --graph --all ``` -Git will open an editor with a default merge message. You can accept it or customize it. +You should see your feature branch merged into main! -**Done!** Your merge is complete! +### Part 5: Practice More -### Common Mistakes to Avoid - -❌ **Forgetting to remove conflict markers** -```json -<<<<<<< HEAD ← Don't leave these in! - "timeout": 5000, - "debug": true ->>>>>>> update-config ← Don't leave these in! -``` -This breaks your code! Always remove ALL markers. - -❌ **Committing without staging** -```bash -git commit # Error! You didn't add the file -``` -Always `git add` the resolved file first! - -❌ **Keeping only one side when both are needed** -If you delete one setting, you lose that work. For this challenge, you need BOTH! - -❌ **Breaking syntax** -```json -"timeout": 5000 ← Missing comma! -"debug": true -``` -Always verify your file is valid after resolving! - -### Aborting a Merge - -Changed your mind? You can abort the merge anytime: +Create additional branches to practice: ```bash -git merge --abort +# Create another feature +git switch -c another-feature + +# Add changes and commits +# ... your work ... + +# Merge it +git switch main +git merge another-feature ``` -This returns your repository to the state before you started the merge. No harm done! +**Verify your work:** +```bash +# From the module directory (not inside challenge/) +.\verify.ps1 +``` -### Useful Commands +## Understanding Branches + +### What is a Branch? + +A **branch** is a lightweight movable pointer to a commit. When you create a branch, Git creates a new pointer - it doesn't copy all your files! + +``` +main: A---B---C + \ +my-feature: D---E +``` + +- Both branches share commits A and B +- Main has commit C +- My-feature has commits D and E +- They're independent! + +### What is HEAD? + +`HEAD` points to your current branch. It's Git's way of saying "you are here." ```bash -# Attempt a merge -git merge +# HEAD points to main +git switch main -# Check which files have conflicts -git status - -# Abort the merge and start over -git merge --abort - -# After resolving conflicts: -git add -git commit - -# View conflicts in a different style -git diff --ours # Your changes -git diff --theirs # Their changes -git diff --base # Original version +# Now HEAD points to my-feature +git switch my-feature ``` -### Pro Tips +## Understanding Merging -💡 **Prevent conflicts** -- Pull changes frequently: `git pull` -- Communicate with your team about who's working on what -- Keep branches short-lived and merge often +### Types of Merges -💡 **Make conflicts easier** -- Work on different files when possible -- If you must edit the same file, coordinate with teammates -- Make small, focused commits +**Three-way merge** (most common): +``` +Before: +main: A---B---C + \ +feature: D---E -💡 **When stuck** -- Read the conflict markers carefully -- Look at `git log` to understand what each side changed -- Ask a teammate to review your resolution -- Use a merge tool: `git mergetool` +After merge: +main: A---B---C---M + \ / +feature: D---E +``` ---- +Git creates a merge commit `M` that has two parents (C and E). -## Complete Command Reference +**Fast-forward merge**: +``` +Before: +main: A---B + \ +feature: C---D + +After merge: +main: A---B---C---D +``` + +If main hasn't changed, Git just moves the pointer forward. No merge commit needed! + +## Key Commands ### Branching ```bash -git branch # List all branches -git branch feature-name # Create a new branch -git switch branch-name # Switch to a branch -git switch -c feature-name # Create and switch -git switch - # Switch to previous branch -git branch -d feature-name # Delete branch (if merged) -git branch -D feature-name # Force delete branch +# List all branches (* shows current branch) +git branch + +# Create a new branch +git branch feature-name + +# Create AND switch to new branch +git switch -c feature-name + +# Switch to existing branch +git switch branch-name + +# Switch to previous branch +git switch - + +# Delete a branch (only if merged) +git branch -d feature-name + +# Force delete a branch +git branch -D feature-name ``` ### Merging ```bash -git merge branch-name # Merge a branch into current branch -git merge --no-ff branch-name # Force a merge commit -git merge --abort # Abort a merge in progress -git log --merges # View only merge commits +# Merge a branch into your current branch +git merge branch-name + +# Abort a merge if something goes wrong +git merge --abort + +# Force a merge commit (even if fast-forward possible) +git merge --no-ff branch-name ``` ### Viewing History ```bash -git log --oneline --graph --all # Visual branch structure -git log --oneline # Compact commit list -git log --graph --decorate --all # Detailed branch view -git log main..feature-login # Commits in feature not in main -git diff main...feature-login # Changes between branches +# Visual branch graph +git log --oneline --graph --all + +# Compact history +git log --oneline + +# Only merge commits +git log --merges + +# Show which branches have been merged into main +git branch --merged main + +# Show which branches haven't been merged +git branch --no-merged main ``` -### Conflict Resolution +## Common Workflows + +### Creating a Feature ```bash -git status # See conflicted files -git diff # View conflicts -git add resolved-file # Mark file as resolved -git commit # Complete the merge -git merge --abort # Give up and start over +# Start from main +git switch main + +# Create feature branch +git switch -c feature-awesome + +# Make changes +echo "cool stuff" > feature.txt +git add . +git commit -m "Add awesome feature" + +# More changes... +echo "more cool stuff" >> feature.txt +git add . +git commit -m "Improve awesome feature" ``` -### Checkpoint Commands (This Module) +### Merging a Feature ```bash -.\reset.ps1 # Show available checkpoints -.\reset.ps1 start # Jump to Checkpoint 1 -.\reset.ps1 merge # Jump to Checkpoint 2 -.\reset.ps1 merge-conflict # Jump to Checkpoint 3 -.\verify.ps1 # Verify all complete -.\verify.ps1 start # Verify Checkpoint 1 -.\verify.ps1 merge # Verify Checkpoint 2 -.\verify.ps1 merge-conflict # Verify Checkpoint 3 +# Switch to main +git switch main + +# Merge your feature +git merge feature-awesome + +# Delete the feature branch (optional) +git branch -d feature-awesome ``` ---- +### Keeping Main Updated While Working + +```bash +# You're on feature-awesome +git switch feature-awesome + +# Main branch has new commits from others +# Bring those into your feature branch +git switch main +git pull +git switch feature-awesome +git merge main + +# Or in one command (more advanced): +git pull origin main +``` ## Troubleshooting ### "I'm on the wrong branch!" ```bash -git switch main # Switch to main -git branch # Verify current branch +# Switch to the correct branch +git switch correct-branch + +# Check current branch anytime +git branch ``` ### "I made commits on the wrong branch!" -Don't panic! You can move them: +Don't panic! You can move commits to another branch: ```bash -# You're on main but should be on feature-login -git switch feature-login # Switch to correct branch -git merge main # Bring the commits over -git switch main -git reset --hard HEAD~1 # Remove from main (careful!) +# Create the correct branch from current state +git branch correct-branch + +# Switch to wrong branch and remove the commits +git switch wrong-branch +git reset --hard HEAD~2 # Remove last 2 commits (adjust number) + +# Switch to correct branch - commits are there! +git switch correct-branch ``` -Or use cherry-pick (covered in a later module). - -### "The merge created a mess!" - -Abort and try again: +### "The merge created unexpected results!" ```bash +# Undo the merge git merge --abort -git status # Verify you're back to clean state + +# Or if already committed: +git reset --hard HEAD~1 ``` -### "I want to start this checkpoint over!" - -Use the reset script: +### "I want to see what changed in a merge!" ```bash -.\reset.ps1 start # Go back to Checkpoint 1 +# Show the merge commit +git show + +# Compare two branches before merging +git diff main..feature-branch ``` -This resets your repository to the beginning of that checkpoint. +## Tips for Success -### "I can't find my branch!" +💡 **Branch often** - Branches are cheap! Create one for each feature or experiment. -List all branches: +💡 **Commit before switching** - Always commit (or stash) changes before switching branches. -```bash -git branch --all # Shows all branches including remote -``` +💡 **Keep branches focused** - One feature per branch makes merging easier. -The branch might have been deleted after merging (this is normal!). +💡 **Delete merged branches** - Clean up with `git branch -d branch-name` after merging. -### "How do I know which checkpoint I'm on?" +💡 **Use descriptive names** - `feature-login` is better than `stuff` or `branch1`. -```bash -.\reset.ps1 # Shows current checkpoint -git log --oneline --graph --all --decorate # Shows all tags/branches -``` - ---- - -## Real-World Workflow Example - -Here's how professional developers use these skills: - -**Day 1: Start a new feature** -```bash -git switch main -git pull # Get latest changes -git switch -c feature-dark-mode # New feature branch -# ... make changes ... -git add . -git commit -m "Add dark mode toggle" -``` - -**Day 2: Continue work** -```bash -git switch feature-dark-mode # Resume work -# ... make more changes ... -git add . -git commit -m "Add dark mode styles" -``` - -**Day 3: Ready to merge** -```bash -git switch main -git pull # Get latest main -git switch feature-dark-mode -git merge main # Bring main's changes into feature -# Resolve any conflicts -git switch main -git merge feature-dark-mode # Merge feature into main -git push # Share with team -git branch -d feature-dark-mode # Clean up -``` - -**This is exactly what you just practiced!** - ---- +💡 **Visualize often** - Run `git log --oneline --graph --all` to understand your history. ## What You've Learned -By completing all three checkpoints, you now understand: +After completing this module, you understand: -### Checkpoint 1: Branching Basics - ✅ Branches create independent lines of development -- ✅ `git switch -c` creates and switches to a new branch +- ✅ `git switch -c` creates a new branch - ✅ Changes in one branch don't affect others -- ✅ Branches are lightweight and easy to create - -### Checkpoint 2: Merging Branches -- ✅ Merging combines work from two branches +- ✅ `git merge` combines branches - ✅ Merge commits have two parent commits -- ✅ `git merge` brings changes into your current branch -- ✅ Three-way merges create a merge commit - -### Checkpoint 3: Resolving Merge Conflicts -- ✅ Conflicts happen when the same lines are changed differently -- ✅ Conflict markers show both versions -- ✅ You choose what the final code should look like -- ✅ Conflicts are normal and easy to resolve with practice - ---- +- ✅ `git log --graph` visualizes branch history +- ✅ Branches are pointers, not copies of files ## Next Steps -**Completed the module?** Great work! You're ready to move on. +Ready to continue? The next module covers **merge conflicts** - what happens when Git can't automatically merge changes. -**Want more practice?** Jump to any checkpoint and try again: +To start over: ```bash -.\reset.ps1 start # Practice branching -.\reset.ps1 merge # Practice merging -.\reset.ps1 merge-conflict # Practice conflict resolution +.\reset.ps1 +.\setup.ps1 ``` -**Ready for the next module?** -Continue to Module 04 to learn about cherry-picking specific commits! - ---- - -**Need help?** Review the relevant checkpoint section above, or run `git status` to see what Git suggests! +**Need help?** Review the commands above, or run `git status` to see what Git suggests! diff --git a/01-essentials/03-branching-and-merging/challenge b/01-essentials/03-branching-and-merging/challenge new file mode 160000 index 0000000..8ae52cc --- /dev/null +++ b/01-essentials/03-branching-and-merging/challenge @@ -0,0 +1 @@ +Subproject commit 8ae52cc25c2a5417b1d6b5316949394da02f6914 diff --git a/01-essentials/03-branching-and-merging/reset.ps1 b/01-essentials/03-branching-and-merging/reset.ps1 index 7027dc4..664d9c5 100755 --- a/01-essentials/03-branching-and-merging/reset.ps1 +++ b/01-essentials/03-branching-and-merging/reset.ps1 @@ -1,216 +1,23 @@ #!/usr/bin/env pwsh <# .SYNOPSIS - Resets the challenge environment to a specific checkpoint. + Resets the Module 03 challenge environment. .DESCRIPTION - This script allows you to jump to any checkpoint in the module, - resetting your repository to that state. Useful for skipping ahead, - starting over, or practicing specific sections. - -.PARAMETER Checkpoint - The checkpoint to reset to: start, merge, or merge-conflict. - If not specified, displays help information. - -.EXAMPLE - .\reset.ps1 - Shows available checkpoints and current status. - -.EXAMPLE - .\reset.ps1 start - Resets to the beginning (branching basics section). - -.EXAMPLE - .\reset.ps1 merge - Jumps to the merging section (feature-login branch already exists). - -.EXAMPLE - .\reset.ps1 merge-conflict - Jumps to the conflict resolution section (merge already complete). + This script removes the challenge directory, allowing you to start fresh. + Run setup.ps1 again after resetting to recreate the environment. #> -param( - [ValidateSet('start', 'merge', 'merge-conflict', '')] - [string]$Checkpoint = '' -) +Write-Host "`n=== Resetting Module 03 Challenge ===" -ForegroundColor Cyan -# Checkpoint to tag mapping -$checkpointTags = @{ - 'start' = 'checkpoint-start' - 'merge' = 'checkpoint-merge' - 'merge-conflict' = 'checkpoint-merge-conflict' +if (Test-Path "challenge") { + Write-Host "Removing challenge directory..." -ForegroundColor Yellow + Remove-Item -Recurse -Force "challenge" + Write-Host "`n[SUCCESS] Challenge environment reset complete!" -ForegroundColor Green + Write-Host "`nRun .\setup.ps1 to create a fresh challenge environment." -ForegroundColor Cyan + Write-Host "" +} else { + Write-Host "`n[INFO] No challenge directory found. Nothing to reset." -ForegroundColor Yellow + Write-Host "Run .\setup.ps1 to create the challenge environment." -ForegroundColor Cyan + Write-Host "" } - -# Checkpoint descriptions -$checkpointDescriptions = @{ - 'start' = 'Branching Basics - Create and work with feature branches' - 'merge' = 'Merging Branches - Merge feature-login into main' - 'merge-conflict' = 'Resolving Conflicts - Fix merge conflicts in config.json' -} - -# ============================================================================ -# Display help if no checkpoint specified -# ============================================================================ -if ($Checkpoint -eq '') { - Write-Host "`n=== Module 03: Branching and Merging - Checkpoints ===" -ForegroundColor Cyan - Write-Host "`nAvailable checkpoints:" -ForegroundColor White - Write-Host "" - - foreach ($key in @('start', 'merge', 'merge-conflict')) { - $desc = $checkpointDescriptions[$key] - Write-Host " $key" -ForegroundColor Green -NoNewline - Write-Host " - $desc" -ForegroundColor White - } - - Write-Host "`nUsage:" -ForegroundColor Cyan - Write-Host " .\reset.ps1 " -ForegroundColor White - Write-Host "" - Write-Host "Examples:" -ForegroundColor Cyan - Write-Host " .\reset.ps1 start # Start from the beginning" -ForegroundColor White - Write-Host " .\reset.ps1 merge # Jump to merging section" -ForegroundColor White - Write-Host " .\reset.ps1 merge-conflict # Jump to conflict resolution" -ForegroundColor White - Write-Host "" - - # Try to detect current checkpoint - if (Test-Path "challenge/.git") { - Push-Location "challenge" - $currentBranch = git branch --show-current 2>$null - $currentCommit = git rev-parse HEAD 2>$null - - # Check which checkpoint we're at - $currentCheckpoint = $null - foreach ($cp in @('start', 'merge', 'merge-conflict')) { - $tagCommit = git rev-parse $checkpointTags[$cp] 2>$null - if ($currentCommit -eq $tagCommit) { - $currentCheckpoint = $cp - break - } - } - - if ($currentCheckpoint) { - Write-Host "Current checkpoint: " -ForegroundColor Yellow -NoNewline - Write-Host "$currentCheckpoint" -ForegroundColor Green -NoNewline - Write-Host " (on branch $currentBranch)" -ForegroundColor Yellow - } else { - Write-Host "Current status: " -ForegroundColor Yellow -NoNewline - Write-Host "In progress (on branch $currentBranch)" -ForegroundColor White - } - - Pop-Location - } - - Write-Host "" - exit 0 -} - -# ============================================================================ -# Validate challenge directory exists -# ============================================================================ -if (-not (Test-Path "challenge")) { - Write-Host "[ERROR] Challenge directory not found." -ForegroundColor Red - Write-Host "Run .\setup.ps1 first to create the challenge environment." -ForegroundColor Yellow - exit 1 -} - -if (-not (Test-Path "challenge/.git")) { - Write-Host "[ERROR] No git repository found in challenge directory." -ForegroundColor Red - Write-Host "Run .\setup.ps1 first to create the challenge environment." -ForegroundColor Yellow - exit 1 -} - -# Navigate to challenge directory -Push-Location "challenge" - -# ============================================================================ -# Verify the checkpoint tag exists -# ============================================================================ -$targetTag = $checkpointTags[$Checkpoint] -$tagExists = git tag -l $targetTag - -if (-not $tagExists) { - Write-Host "[ERROR] Checkpoint tag '$targetTag' not found." -ForegroundColor Red - Write-Host "Run ..\setup.ps1 to recreate the challenge environment." -ForegroundColor Yellow - Pop-Location - exit 1 -} - -# ============================================================================ -# Check for uncommitted changes -# ============================================================================ -$statusOutput = git status --porcelain 2>$null - -if ($statusOutput) { - Write-Host "`n[WARNING] You have uncommitted changes!" -ForegroundColor Yellow - Write-Host "The following changes will be lost:" -ForegroundColor Yellow - Write-Host "" - git status --short - Write-Host "" - - $response = Read-Host "Continue and discard all changes? (y/N)" - if ($response -ne 'y' -and $response -ne 'Y') { - Write-Host "`nReset cancelled." -ForegroundColor Cyan - Pop-Location - exit 0 - } -} - -# ============================================================================ -# Reset to checkpoint -# ============================================================================ -Write-Host "`nResetting to checkpoint: $Checkpoint" -ForegroundColor Cyan -Write-Host "Description: $($checkpointDescriptions[$Checkpoint])" -ForegroundColor White -Write-Host "" - -try { - # Reset to the checkpoint tag - git reset --hard $targetTag 2>&1 | Out-Null - - # Clean untracked files - git clean -fd 2>&1 | Out-Null - - # Ensure we're on main branch - $currentBranch = git branch --show-current - if ($currentBranch -ne 'main') { - git switch main 2>&1 | Out-Null - git reset --hard $targetTag 2>&1 | Out-Null - } - - Write-Host "[SUCCESS] Reset to checkpoint '$Checkpoint' complete!" -ForegroundColor Green - Write-Host "" - - # Show what to do next - switch ($Checkpoint) { - 'start' { - Write-Host "Next steps:" -ForegroundColor Cyan - Write-Host " 1. Create a new branch: git switch -c feature-login" -ForegroundColor White - Write-Host " 2. Create login.py and make 2+ commits" -ForegroundColor White - Write-Host " 3. Verify: ..\verify.ps1 start" -ForegroundColor White - } - 'merge' { - Write-Host "Next steps:" -ForegroundColor Cyan - Write-Host " 1. View branch structure: git log --oneline --graph --all" -ForegroundColor White - Write-Host " 2. Merge feature-login: git merge feature-login" -ForegroundColor White - Write-Host " 3. Verify: ..\verify.ps1 merge" -ForegroundColor White - } - 'merge-conflict' { - Write-Host "Next steps:" -ForegroundColor Cyan - Write-Host " 1. Attempt merge: git merge update-config" -ForegroundColor White - Write-Host " 2. Resolve conflicts in config.json" -ForegroundColor White - Write-Host " 3. Complete merge: git add config.json && git commit" -ForegroundColor White - Write-Host " 4. Verify: ..\verify.ps1 merge-conflict" -ForegroundColor White - } - } - - Write-Host "" - Write-Host "View current state: git log --oneline --graph --all" -ForegroundColor Cyan - Write-Host "" - -} catch { - Write-Host "[ERROR] Failed to reset to checkpoint." -ForegroundColor Red - Write-Host $_.Exception.Message -ForegroundColor Red - Pop-Location - exit 1 -} - -Pop-Location -exit 0 diff --git a/01-essentials/03-branching-and-merging/setup.ps1 b/01-essentials/03-branching-and-merging/setup.ps1 index 8d15534..a9ca504 100755 --- a/01-essentials/03-branching-and-merging/setup.ps1 +++ b/01-essentials/03-branching-and-merging/setup.ps1 @@ -1,17 +1,13 @@ #!/usr/bin/env pwsh <# .SYNOPSIS - Sets up the Module 03 checkpoint-based challenge environment. + Sets up the Module 03 challenge environment for branching and merging. .DESCRIPTION - This script creates a challenge directory with a complete Git repository - containing all commits and checkpoints for learning branching, merging, - and merge conflict resolution in one continuous workflow. - - The script creates three checkpoints: - - checkpoint-start: Beginning of branching basics - - checkpoint-merge: Beginning of merging section - - checkpoint-merge-conflict: Beginning of conflict resolution + This script creates a challenge directory with a Git repository containing + a realistic project history with multiple merged branches. Students will see + what branching and merging looks like in practice, then create their own + branches to experiment. #> Write-Host "`n=== Setting up Module 03: Branching and Merging ===" -ForegroundColor Cyan @@ -36,113 +32,75 @@ git config user.name "Workshop Student" git config user.email "student@example.com" # ============================================================================ -# PHASE 1: Branching Basics - Initial commits on main +# Create a realistic project history with multiple merged branches # ============================================================================ -Write-Host "`nPhase 1: Creating initial project structure..." -ForegroundColor Cyan +Write-Host "Creating project history with multiple branches..." -ForegroundColor Cyan -# Commit 1: Initial commit -$mainContent = @" -# main.py - Main application file +# Initial commits on main +$readmeContent = @" +# My Application -def main(): - print("Welcome to the Application!") - print("This is the main branch") - -if __name__ == "__main__": - main() +A sample application for learning Git branching and merging. "@ -Set-Content -Path "main.py" -Value $mainContent +Set-Content -Path "README.md" -Value $readmeContent git add . git commit -m "Initial commit" | Out-Null -# Commit 2: Add main functionality $mainContent = @" # main.py - Main application file def main(): print("Welcome to the Application!") - print("This is the main branch") - run_application() - -def run_application(): - print("Application is running...") - print("Ready for new features!") if __name__ == "__main__": main() "@ Set-Content -Path "main.py" -Value $mainContent git add . -git commit -m "Add main functionality" | Out-Null - -# Tag checkpoint-start (students begin here - will create feature-login) -Write-Host "Creating checkpoint: start" -ForegroundColor Green -git tag checkpoint-start +git commit -m "Add main application file" | Out-Null # ============================================================================ -# PHASE 2: Create feature-login branch (what students will do in checkpoint 1) +# Branch 1: feature-login (will be merged) # ============================================================================ -Write-Host "Phase 2: Creating feature-login branch..." -ForegroundColor Cyan - -# Create and switch to feature-login branch +Write-Host "Creating feature-login branch..." -ForegroundColor Cyan git switch -c feature-login | Out-Null -# Commit 3: Add login module $loginContent = @" -# login.py - User login module +# login.py - User authentication def login(username, password): """Authenticate a user.""" - print(f"Authenticating user: {username}") - # TODO: Add actual authentication logic - return True - -def logout(username): - """Log out a user.""" - print(f"Logging out user: {username}") + print(f"Logging in user: {username}") return True "@ Set-Content -Path "login.py" -Value $loginContent git add . git commit -m "Add login module" | Out-Null -# Commit 4: Add password validation $loginContent = @" -# login.py - User login module +# login.py - User authentication def validate_password(password): """Validate password strength.""" - if len(password) < 8: - return False - return True + return len(password) >= 8 def login(username, password): """Authenticate a user.""" if not validate_password(password): print("Password too weak!") return False - print(f"Authenticating user: {username}") - # TODO: Add actual authentication logic - return True - -def logout(username): - """Log out a user.""" - print(f"Logging out user: {username}") + print(f"Logging in user: {username}") return True "@ Set-Content -Path "login.py" -Value $loginContent git add . git commit -m "Add password validation" | Out-Null -# Switch back to main +# Switch back to main and make more commits git switch main | Out-Null -# Now create divergence - add commits to main while feature-login exists -Write-Host "Creating divergent history on main..." -ForegroundColor Cyan - -# Commit 5: Add app.py with basic functionality $appContent = @" -# app.py - Main application entry point +# app.py - Application entry point from main import main @@ -150,7 +108,6 @@ def run(): """Run the application.""" print("Starting application...") main() - print("Application finished.") if __name__ == "__main__": run() @@ -159,16 +116,65 @@ Set-Content -Path "app.py" -Value $appContent git add . git commit -m "Add app.py entry point" | Out-Null -# Commit 6: Add README +# Merge feature-login into main +Write-Host "Merging feature-login into main..." -ForegroundColor Green +git merge feature-login --no-edit | Out-Null + +# ============================================================================ +# Branch 2: feature-api (will be merged) +# ============================================================================ +Write-Host "Creating feature-api branch..." -ForegroundColor Cyan +git switch -c feature-api | Out-Null + +$apiContent = @" +# api.py - API endpoints + +def get_data(): + """Retrieve data from API.""" + return {"status": "ok", "data": []} + +def post_data(data): + """Send data to API.""" + print(f"Posting data: {data}") + return {"status": "ok"} +"@ +Set-Content -Path "api.py" -Value $apiContent +git add . +git commit -m "Add API module" | Out-Null + +$apiContent = @" +# api.py - API endpoints + +def get_data(): + """Retrieve data from API.""" + return {"status": "ok", "data": []} + +def post_data(data): + """Send data to API.""" + print(f"Posting data: {data}") + return {"status": "ok"} + +def delete_data(id): + """Delete data by ID.""" + print(f"Deleting data: {id}") + return {"status": "ok"} +"@ +Set-Content -Path "api.py" -Value $apiContent +git add . +git commit -m "Add delete endpoint to API" | Out-Null + +# Switch back to main and add documentation +git switch main | Out-Null + $readmeContent = @" # My Application -Welcome to my application! +A sample application for learning Git branching and merging. ## Features -- Main functionality -- More features coming soon +- User authentication +- Main application logic ## Setup @@ -176,104 +182,107 @@ Run: python app.py "@ Set-Content -Path "README.md" -Value $readmeContent git add . -git commit -m "Add README documentation" | Out-Null +git commit -m "Update README with setup instructions" | Out-Null -# Tag checkpoint-merge (students begin merging here - divergent branches ready) -Write-Host "Creating checkpoint: merge" -ForegroundColor Green -git tag checkpoint-merge +# Merge feature-api into main +Write-Host "Merging feature-api into main..." -ForegroundColor Green +git merge feature-api --no-edit | Out-Null # ============================================================================ -# PHASE 3: Merge feature-login into main (what students will do in checkpoint 2) +# Branch 3: feature-database (will be merged) # ============================================================================ -Write-Host "Phase 3: Merging feature-login into main..." -ForegroundColor Cyan +Write-Host "Creating feature-database branch..." -ForegroundColor Cyan +git switch -c feature-database | Out-Null -# Merge feature-login into main (will create three-way merge commit) -git merge feature-login --no-edit | Out-Null +$dbContent = @" +# database.py - Database operations -# ============================================================================ -# PHASE 4: Create conflict scenario (what students will do in checkpoint 3) -# ============================================================================ -Write-Host "Phase 4: Creating merge conflict scenario..." -ForegroundColor Cyan +class Database: + def __init__(self): + self.connection = None -# Create config.json file on main -$initialConfig = @" -{ - "app": { - "name": "MyApp", - "version": "1.0.0", - "port": 3000 - } -} + def connect(self): + """Connect to database.""" + print("Connecting to database...") + self.connection = True + + def query(self, sql): + """Execute SQL query.""" + print(f"Executing: {sql}") + return [] "@ -Set-Content -Path "config.json" -Value $initialConfig -git add config.json -git commit -m "Add initial configuration" | Out-Null +Set-Content -Path "database.py" -Value $dbContent +git add . +git commit -m "Add database module" | Out-Null -# On main branch: Add timeout setting -$mainConfig = @" -{ - "app": { - "name": "MyApp", - "version": "1.0.0", - "port": 3000, - "timeout": 5000 - } -} +$dbContent = @" +# database.py - Database operations + +class Database: + def __init__(self): + self.connection = None + + def connect(self): + """Connect to database.""" + print("Connecting to database...") + self.connection = True + + def disconnect(self): + """Disconnect from database.""" + print("Disconnecting from database...") + self.connection = None + + def query(self, sql): + """Execute SQL query.""" + print(f"Executing: {sql}") + return [] "@ -Set-Content -Path "config.json" -Value $mainConfig -git add config.json -git commit -m "Add timeout configuration" | Out-Null +Set-Content -Path "database.py" -Value $dbContent +git add . +git commit -m "Add disconnect method" | Out-Null -# Create update-config branch from the commit before timeout was added -git switch -c update-config HEAD~1 | Out-Null - -# On update-config branch: Add debug setting (conflicting change) -$featureConfig = @" -{ - "app": { - "name": "MyApp", - "version": "1.0.0", - "port": 3000, - "debug": true - } -} -"@ -Set-Content -Path "config.json" -Value $featureConfig -git add config.json -git commit -m "Add debug mode configuration" | Out-Null - -# Switch back to main +# Switch to main and merge git switch main | Out-Null +Write-Host "Merging feature-database into main..." -ForegroundColor Green +git merge feature-database --no-edit | Out-Null -# Tag checkpoint-merge-conflict (students begin conflict resolution here - on main with timeout, update-config has debug) -Write-Host "Creating checkpoint: merge-conflict" -ForegroundColor Green -git tag checkpoint-merge-conflict +# Final update on main +$readmeContent = @" +# My Application -# ============================================================================ -# Reset to checkpoint-start so students begin at the beginning -# ============================================================================ -Write-Host "`nResetting to checkpoint-start..." -ForegroundColor Yellow -git reset --hard checkpoint-start | Out-Null -git clean -fd | Out-Null +A sample application for learning Git branching and merging. + +## Features + +- User authentication +- API endpoints +- Database operations +- Main application logic + +## Setup + +Run: python app.py + +## Requirements + +- Python 3.6+ +"@ +Set-Content -Path "README.md" -Value $readmeContent +git add . +git commit -m "Update README with all features" | 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 "`nThis module uses a CHECKPOINT SYSTEM:" -ForegroundColor Yellow -Write-Host " You'll work through 3 sections in one continuous repository:" -ForegroundColor White -Write-Host " 1. Branching Basics (checkpoint: start)" -ForegroundColor White -Write-Host " 2. Merging Branches (checkpoint: merge)" -ForegroundColor White -Write-Host " 3. Resolving Merge Conflicts (checkpoint: merge-conflict)" -ForegroundColor White -Write-Host "`nCommands:" -ForegroundColor Cyan -Write-Host " .\reset.ps1 - Show available checkpoints" -ForegroundColor White -Write-Host " .\reset.ps1 start - Jump to branching section" -ForegroundColor White -Write-Host " .\reset.ps1 merge - Jump to merging section" -ForegroundColor White -Write-Host " .\verify.ps1 - Verify all sections complete" -ForegroundColor White -Write-Host " .\verify.ps1 start - Verify only branching section" -ForegroundColor White +Write-Host "`nThe repository contains a realistic project history:" -ForegroundColor Yellow +Write-Host " - Multiple feature branches (login, api, database)" -ForegroundColor White +Write-Host " - All branches have been merged into main" -ForegroundColor White +Write-Host " - View the history: git log --oneline --graph --all" -ForegroundColor White Write-Host "`nNext steps:" -ForegroundColor Cyan Write-Host " 1. Read the README.md for detailed instructions" -ForegroundColor White Write-Host " 2. cd challenge" -ForegroundColor White -Write-Host " 3. Start with Checkpoint 1: Branching Basics" -ForegroundColor White +Write-Host " 3. Explore the repository history: git log --oneline --graph --all" -ForegroundColor White +Write-Host " 4. Create your own branches and practice merging!" -ForegroundColor White Write-Host "" diff --git a/01-essentials/03-branching-and-merging/verify.ps1 b/01-essentials/03-branching-and-merging/verify.ps1 index 8311d87..246f8fe 100755 --- a/01-essentials/03-branching-and-merging/verify.ps1 +++ b/01-essentials/03-branching-and-merging/verify.ps1 @@ -1,34 +1,15 @@ #!/usr/bin/env pwsh <# .SYNOPSIS - Verifies the Module 03 challenge solution (checkpoint-aware). + Verifies the Module 03 challenge solution. .DESCRIPTION - This script can verify completion of individual checkpoints or - the entire module. Without arguments, it verifies all checkpoints. - -.PARAMETER Checkpoint - The checkpoint to verify: start, merge, or merge-conflict. - If not specified, verifies all checkpoints. - -.EXAMPLE - .\verify.ps1 - Verifies all three checkpoints are complete. - -.EXAMPLE - .\verify.ps1 start - Verifies only the branching basics checkpoint. - -.EXAMPLE - .\verify.ps1 merge - Verifies only the merging checkpoint. + This script checks that you've practiced branching and merging by: + - Creating at least one new branch (beyond the example branches) + - Making commits on your branch + - Merging your branch into main #> -param( - [ValidateSet('start', 'merge', 'merge-conflict', '')] - [string]$Checkpoint = '' -) - $script:allChecksPassed = $true # ============================================================================ @@ -51,198 +32,15 @@ function Write-Hint { Write-Host "[HINT] $Message" -ForegroundColor Yellow } -# ============================================================================ -# Checkpoint 1: Branching Basics Verification -# ============================================================================ - -function Verify-Branching { - Write-Host "`n=== Checkpoint 1: Branching Basics ===" -ForegroundColor Cyan - - # Save current branch - $originalBranch = git branch --show-current 2>$null - - # Check if feature-login branch exists - $branchExists = git branch --list "feature-login" 2>$null - if ($branchExists) { - Write-Pass "Branch 'feature-login' exists" - } else { - Write-Fail "Branch 'feature-login' not found" - Write-Hint "Create the branch with: git switch -c feature-login" - return - } - - # Check if feature-login has commits beyond main (or if they've been merged) - $commitCount = git rev-list main..feature-login --count 2>$null - $mergeCommitExists = (git log --merges --oneline 2>$null | Select-String "Merge.*feature-login") - - if ($mergeCommitExists -and $commitCount -eq 0) { - # Commits were merged into main - this is correct! - Write-Pass "Branch 'feature-login' commits have been merged into main" - } elseif ($commitCount -ge 2) { - Write-Pass "Branch 'feature-login' has $commitCount new commits" - } else { - Write-Fail "Branch 'feature-login' needs at least 2 new commits (found: $commitCount)" - Write-Hint "Make sure you've committed login.py and made at least one more commit" - } - - # Switch to feature-login and check for login.py - git switch feature-login 2>$null | Out-Null - if (Test-Path "login.py") { - Write-Pass "File 'login.py' exists in feature-login branch" - } else { - Write-Fail "File 'login.py' not found in feature-login branch" - Write-Hint "Create login.py and commit it to the feature-login branch" - } - - # Switch to main and verify login.py doesn't exist there yet (unless merged) - git switch main 2>$null | Out-Null - - # Check if merge happened - if so, login.py can exist on main - $mergeCommitExists = (git log --merges --oneline 2>$null | Select-String "Merge.*feature-login") - - if (-not $mergeCommitExists) { - # No merge yet - login.py should NOT be on main - if (-not (Test-Path "login.py")) { - Write-Pass "File 'login.py' does NOT exist in main branch (branches are independent!)" - } else { - Write-Fail "File 'login.py' should not exist in main branch yet (before merge)" - Write-Hint "Make sure you created login.py only on the feature-login branch" - } - } - - # Switch back to original branch - if ($originalBranch) { - git switch $originalBranch 2>$null | Out-Null - } +function Write-Info { + param([string]$Message) + Write-Host "[INFO] $Message" -ForegroundColor Cyan } # ============================================================================ -# Checkpoint 2: Merging Verification +# Check challenge directory exists # ============================================================================ -function Verify-Merging { - Write-Host "`n=== Checkpoint 2: Merging Branches ===" -ForegroundColor Cyan - - # Check current branch is main - $currentBranch = git branch --show-current 2>$null - if ($currentBranch -eq "main") { - Write-Pass "Currently on main branch" - } else { - Write-Fail "Should be on main branch (currently on: $currentBranch)" - Write-Hint "Switch to main with: git switch main" - } - - # Check if login.py exists on main (indicates merge happened) - if (Test-Path "login.py") { - Write-Pass "File 'login.py' exists on main branch (merged successfully)" - } else { - Write-Fail "File 'login.py' not found on main branch" - Write-Hint "Merge feature-login into main with: git merge feature-login" - } - - # Check for merge commit - $mergeCommitExists = (git log --merges --oneline 2>$null | Select-String "Merge.*feature-login") - - if ($mergeCommitExists) { - Write-Pass "Merge commit exists" - } else { - Write-Fail "No merge commit found" - Write-Hint "Create a merge commit with: git merge feature-login" - } - - # Check commit count (should have both branches' commits) - $commitCount = [int](git rev-list --count HEAD 2>$null) - if ($commitCount -ge 6) { - Write-Pass "Repository has $commitCount commits (merge complete)" - } else { - Write-Fail "Repository should have at least 6 commits after merge (found: $commitCount)" - } -} - -# ============================================================================ -# Checkpoint 3: Merge Conflicts Verification -# ============================================================================ - -function Verify-MergeConflicts { - Write-Host "`n=== Checkpoint 3: Resolving Merge Conflicts ===" -ForegroundColor Cyan - - # Check current branch is main - $currentBranch = git branch --show-current 2>$null - if ($currentBranch -eq "main") { - Write-Pass "Currently on main branch" - } else { - Write-Fail "Should be on main branch (currently on: $currentBranch)" - Write-Hint "Switch to main with: git switch main" - } - - # Check that merge is not in progress - if (Test-Path ".git/MERGE_HEAD") { - Write-Fail "Merge is still in progress (conflicts not resolved)" - Write-Hint "Resolve conflicts in config.json, then: git add config.json && git commit" - return - } else { - Write-Pass "No merge in progress (conflicts resolved)" - } - - # Check if config.json exists - if (Test-Path "config.json") { - Write-Pass "File 'config.json' exists" - } else { - Write-Fail "File 'config.json' not found" - Write-Hint "Merge update-config branch with: git merge update-config" - return - } - - # Verify config.json is valid JSON - try { - $configContent = Get-Content "config.json" -Raw - $config = $configContent | ConvertFrom-Json -ErrorAction Stop - Write-Pass "File 'config.json' is valid JSON" - } catch { - Write-Fail "File 'config.json' is not valid JSON" - Write-Hint "Make sure you removed all conflict markers (<<<<<<<, =======, >>>>>>>)" - return - } - - # Check for conflict markers - if ($configContent -match '<<<<<<<|=======|>>>>>>>') { - Write-Fail "Conflict markers still present in config.json" - Write-Hint "Remove all conflict markers (<<<<<<<, =======, >>>>>>>)" - return - } else { - Write-Pass "No conflict markers in config.json" - } - - # Verify both settings are present (timeout and debug) - if ($config.app.timeout -eq 5000) { - Write-Pass "Timeout setting preserved (5000)" - } else { - Write-Fail "Timeout setting missing or incorrect" - Write-Hint "Keep the timeout: 5000 setting from main branch" - } - - if ($config.app.debug -eq $true) { - Write-Pass "Debug setting preserved (true)" - } else { - Write-Fail "Debug setting missing or incorrect" - Write-Hint "Keep the debug: true setting from update-config branch" - } - - # Verify merge commit exists for update-config - $updateConfigMerge = (git log --merges --oneline 2>$null | Select-String "Merge.*update-config") - if ($updateConfigMerge) { - Write-Pass "Merge commit exists for update-config branch" - } else { - Write-Fail "No merge commit found for update-config" - Write-Hint "Complete the merge with: git commit (after resolving conflicts)" - } -} - -# ============================================================================ -# Main Script Logic -# ============================================================================ - -# Check if challenge directory exists if (-not (Test-Path "challenge")) { Write-Host "[ERROR] Challenge directory not found." -ForegroundColor Red Write-Host "Run .\setup.ps1 first to create the challenge environment." -ForegroundColor Yellow @@ -251,7 +49,6 @@ if (-not (Test-Path "challenge")) { Push-Location "challenge" -# Check if git repository exists if (-not (Test-Path ".git")) { Write-Host "[ERROR] Not a git repository." -ForegroundColor Red Write-Host "Run ..\setup.ps1 first to create the challenge environment." -ForegroundColor Yellow @@ -259,62 +56,98 @@ if (-not (Test-Path ".git")) { exit 1 } -# Run appropriate verification -if ($Checkpoint -eq '') { - # Verify all checkpoints - Write-Host "`n=== Verifying All Checkpoints ===" -ForegroundColor Cyan +Write-Host "`n=== Verifying Module 03: Branching and Merging ===" -ForegroundColor Cyan - Verify-Branching - Verify-Merging - Verify-MergeConflicts +# ============================================================================ +# Count initial setup commits (should be 13 commits from setup) +# ============================================================================ +$initialCommitCount = 13 +# ============================================================================ +# Check for new commits beyond setup +# ============================================================================ +Write-Host "`nChecking your work..." -ForegroundColor Cyan + +$totalCommits = [int](git rev-list --count HEAD 2>$null) + +if ($totalCommits -gt $initialCommitCount) { + $newCommits = $totalCommits - $initialCommitCount + Write-Pass "Found $newCommits new commit(s) beyond the initial setup" } else { - # Verify specific checkpoint - switch ($Checkpoint) { - 'start' { Verify-Branching } - 'merge' { Verify-Merging } - 'merge-conflict' { Verify-MergeConflicts } - } + Write-Fail "No new commits found" + Write-Hint "Create a branch, make some commits, and merge it into main" + Write-Hint "Example: git switch -c my-feature" +} + +# ============================================================================ +# Check for branches (excluding the example branches) +# ============================================================================ +$allBranches = git branch --list 2>$null | ForEach-Object { $_.Trim('* ') } +$exampleBranches = @('main', 'feature-login', 'feature-api', 'feature-database') +$studentBranches = $allBranches | Where-Object { $_ -notin $exampleBranches } + +if ($studentBranches.Count -gt 0) { + Write-Pass "Created $($studentBranches.Count) new branch(es): $($studentBranches -join ', ')" +} else { + Write-Info "No new branches found (it's OK if you deleted them after merging)" + Write-Hint "To practice: git switch -c your-branch-name" +} + +# ============================================================================ +# Check for merge commits by the student +# ============================================================================ +$setupUser = "Workshop Student" +$mergeCommits = git log --merges --format="%s" 2>$null + +# Count how many merge commits exist beyond the initial 3 +$totalMerges = ($mergeCommits | Measure-Object).Count +$setupMerges = 3 # feature-login, feature-api, feature-database + +if ($totalMerges -gt $setupMerges) { + $studentMerges = $totalMerges - $setupMerges + Write-Pass "Performed $studentMerges merge(s) of your own work" +} else { + Write-Fail "No merge commits found beyond the example merges" + Write-Hint "Create a branch, add commits, then merge it: git merge your-branch-name" +} + +# ============================================================================ +# Check current branch +# ============================================================================ +$currentBranch = git branch --show-current 2>$null +if ($currentBranch -eq "main") { + Write-Pass "Currently on main branch" +} else { + Write-Info "Currently on '$currentBranch' branch" + Write-Hint "Typically you merge feature branches INTO main" } Pop-Location +# ============================================================================ # Final summary +# ============================================================================ Write-Host "" if ($script:allChecksPassed) { Write-Host "=========================================" -ForegroundColor Green Write-Host " CONGRATULATIONS! CHALLENGE PASSED!" -ForegroundColor Green Write-Host "=========================================" -ForegroundColor Green - - if ($Checkpoint -eq '') { - Write-Host "`nYou've completed the entire module!" -ForegroundColor Cyan - Write-Host "You've mastered:" -ForegroundColor Cyan - Write-Host " ✓ Creating and working with branches" -ForegroundColor White - Write-Host " ✓ Merging branches together" -ForegroundColor White - Write-Host " ✓ Resolving merge conflicts" -ForegroundColor White - Write-Host "`nReady for the next module!" -ForegroundColor Green - } else { - Write-Host "`nCheckpoint '$Checkpoint' complete!" -ForegroundColor Cyan - - switch ($Checkpoint) { - 'start' { - Write-Host "Next: Move to the merging checkpoint" -ForegroundColor White - Write-Host " ..\reset.ps1 merge OR continue to merge feature-login" -ForegroundColor Yellow - } - 'merge' { - Write-Host "Next: Move to the conflict resolution checkpoint" -ForegroundColor White - Write-Host " ..\reset.ps1 merge-conflict" -ForegroundColor Yellow - } - 'merge-conflict' { - Write-Host "Module complete! Ready for the next module!" -ForegroundColor Green - } - } - } + Write-Host "`nYou've successfully practiced:" -ForegroundColor Cyan + Write-Host " ✓ Creating branches" -ForegroundColor White + Write-Host " ✓ Making commits on branches" -ForegroundColor White + Write-Host " ✓ Merging branches together" -ForegroundColor White + Write-Host "`nReady for the next module!" -ForegroundColor Green Write-Host "" exit 0 } else { - Write-Host "[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 "[SUMMARY] Some checks failed. Review the hints above." -ForegroundColor Red + Write-Host "" + Write-Host "Quick guide:" -ForegroundColor Cyan + Write-Host " 1. Create a branch: git switch -c my-feature" -ForegroundColor White + Write-Host " 2. Make changes and commit them" -ForegroundColor White + Write-Host " 3. Switch to main: git switch main" -ForegroundColor White + Write-Host " 4. Merge your branch: git merge my-feature" -ForegroundColor White + Write-Host " 5. Run this verify script again" -ForegroundColor White Write-Host "" exit 1 } diff --git a/01-essentials/04-merge-conflict/README.md b/01-essentials/04-merge-conflict/README.md new file mode 100644 index 0000000..7cdc640 --- /dev/null +++ b/01-essentials/04-merge-conflict/README.md @@ -0,0 +1,487 @@ +# Module 04: Merge Conflicts + +## Learning Objectives + +By the end of this module, you will: +- Understand what merge conflicts are and why they occur +- Use `git diff` to discover changes between branches +- Identify merge conflicts in your repository +- Read and interpret conflict markers (`<<<<<<<`, `=======`, `>>>>>>>`) +- Resolve merge conflicts manually +- Complete a merge after resolving conflicts + +## Setup + +Create the challenge environment: + +```bash +.\setup.ps1 +``` + +This creates a repository with two feature branches that have conflicting changes. + +## Overview + +A **merge conflict** occurs when Git cannot automatically combine changes because both branches modified the same part of the same file in different ways. + +**When do conflicts happen?** +- ✅ Two branches modify the same lines in a file +- ✅ One branch deletes a file that another branch modifies +- ✅ Complex changes Git can't merge automatically +- ❌ Different files are changed (no conflict!) +- ❌ Different parts of the same file are changed (no conflict!) + +**Don't fear conflicts!** They're a normal part of collaborative development. Git just needs your help to decide what the final code should look like. + +## Your Task + +### Part 1: Discover the Changes + +Before merging, it's good practice to see what each branch changed: + +```bash +cd challenge + +# Check which branch you're on +git branch + +# View all branches +git branch --all +``` + +You'll see three branches: `main`, `add-timeout`, and `add-debug`. + +**Discover what each branch changed:** + +```bash +# Compare main with add-timeout +git diff main add-timeout + +# Compare main with add-debug +git diff main add-debug + +# Compare the two feature branches directly +git diff add-timeout add-debug +``` + +**What did you discover?** +- Both branches modified `config.json` +- They both added a line in the same location (after `"port": 3000`) +- One adds `"timeout": 5000` +- The other adds `"debug": true` + +This is a recipe for a conflict! + +### Part 2: Merge the First Branch (No Conflict) + +Let's merge `add-timeout` first: + +```bash +# Make sure you're on main +git switch main + +# Merge the first branch +git merge add-timeout +``` + +✅ **Success!** This merge works because main hasn't changed since add-timeout was created. + +```bash +# View the updated config +cat config.json + +# Check the history +git log --oneline --graph --all +``` + +### Part 3: Try to Merge the Second Branch (Conflict!) + +Now let's try to merge `add-debug`: + +```bash +# Still on main +git merge add-debug +``` + +💥 **Boom!** You'll see: + +``` +Auto-merging config.json +CONFLICT (content): Merge conflict in config.json +Automatic merge failed; fix conflicts and then commit the result. +``` + +**Don't panic!** This is expected. Git is asking for your help. + +### Part 4: Check the Status + +```bash +git status +``` + +You'll see: + +``` +On branch main +You have unmerged paths. + (fix conflicts and run "git commit") + (use "git merge --abort" to abort the merge) + +Unmerged paths: + (use "git add ..." to mark resolution) + both modified: config.json +``` + +This tells you that `config.json` needs your attention! + +### Part 5: Open and Examine the Conflicted File + +Open `config.json` in your text editor: + +```bash +# On Windows +notepad config.json + +# Or use VS Code +code config.json +``` + +You'll see special **conflict markers**: + +```json +{ + "app": { + "name": "MyApp", + "version": "1.0.0", + "port": 3000, +<<<<<<< HEAD + "timeout": 5000 +======= + "debug": true +>>>>>>> add-debug + } +} +``` + +### Part 6: Understand the Conflict Markers + +Let's break down what you're seeing: + +``` +<<<<<<< HEAD + "timeout": 5000 ← Your current branch (main, which has add-timeout merged) +======= + "debug": true ← The branch you're merging (add-debug) +>>>>>>> add-debug +``` + +**What each marker means:** +- `<<<<<<< HEAD` - Start of your changes (current branch) +- `=======` - Separator between the two versions +- `>>>>>>> add-debug` - End of their changes (branch being merged) + +### Part 7: Resolve the Conflict + +You have three options: + +**Option 1: Keep ONLY your changes (timeout)** +```json + "timeout": 5000 +``` + +**Option 2: Keep ONLY their changes (debug)** +```json + "debug": true +``` + +**Option 3: Keep BOTH changes** ← **Do this!** +```json + "timeout": 5000, + "debug": true +``` + +### Part 8: Edit the File + +For this challenge, we want **both settings**, so: + +1. Delete ALL the conflict markers: + - Remove `<<<<<<< HEAD` + - Remove `=======` + - Remove `>>>>>>> add-debug` + +2. Keep both settings: + +**Before (with conflict markers):** +```json +{ + "app": { + "name": "MyApp", + "version": "1.0.0", + "port": 3000, +<<<<<<< HEAD + "timeout": 5000 +======= + "debug": true +>>>>>>> add-debug + } +} +``` + +**After (resolved):** +```json +{ + "app": { + "name": "MyApp", + "version": "1.0.0", + "port": 3000, + "timeout": 5000, + "debug": true + } +} +``` + +**Important:** +- Remove ALL markers +- Add a comma after `"timeout": 5000` (for valid JSON) +- Ensure the file is valid JSON + +3. Save the file + +### Part 9: Mark the Conflict as Resolved + +Tell Git you've resolved the conflict: + +```bash +# Stage the resolved file +git add config.json + +# Check status +git status +``` + +You should see: + +``` +On branch main +All conflicts fixed but you are still merging. + (use "git commit" to conclude merge) +``` + +Perfect! Git confirms the conflict is resolved. + +### Part 10: Complete the Merge + +Commit the merge: + +```bash +git commit +``` + +Git will open an editor with a default merge message. You can accept it or customize it, then save and close. + +**Done!** Your merge is complete! + +```bash +# View the final result +cat config.json + +# View the history +git log --oneline --graph --all +``` + +You should see both `timeout` and `debug` in the config! + +### Part 11: Verify Your Solution + +From the module directory (not inside challenge/): + +```bash +.\verify.ps1 +``` + +## Understanding Conflict Markers + +### Anatomy of a Conflict + +``` +<<<<<<< HEAD ← Marker: Start of your version +Your changes here +======= ← Marker: Separator +Their changes here +>>>>>>> branch-name ← Marker: End of their version +``` + +### Common Conflict Patterns + +**Simple conflict:** +``` +<<<<<<< HEAD +print("Hello") +======= +print("Hi") +>>>>>>> feature +``` +Decision: Which greeting do you want? + +**Both are needed:** +``` +<<<<<<< HEAD +timeout: 5000 +======= +debug: true +>>>>>>> feature +``` +Decision: Keep both (add comma)! + +**Deletion conflict:** +``` +<<<<<<< HEAD +# Function deleted on your branch +======= +def old_function(): + pass +>>>>>>> feature +``` +Decision: Delete or keep the function? + +## Common Mistakes to Avoid + +❌ **Forgetting to remove conflict markers** +```json +<<<<<<< HEAD ← Don't leave these in! + "timeout": 5000, + "debug": true +>>>>>>> add-debug ← Don't leave these in! +``` +This breaks your code! Always remove ALL markers. + +❌ **Committing without staging** +```bash +git commit # Error! You didn't add the file +``` +Always `git add` the resolved file first! + +❌ **Keeping only one side when both are needed** +If you delete one setting, you lose that work! + +❌ **Breaking syntax** +```json +"timeout": 5000 ← Missing comma! +"debug": true +``` +Always verify your file is valid after resolving! + +❌ **Not testing the result** +Always check that your resolved code works! + +## Aborting a Merge + +Changed your mind? You can abort the merge anytime before committing: + +```bash +git merge --abort +``` + +This returns your repository to the state before you started the merge. No harm done! + +## Key Commands + +```bash +# Discover changes before merging +git diff branch1 branch2 + +# Attempt a merge +git merge + +# Check which files have conflicts +git status + +# Abort the merge and start over +git merge --abort + +# After resolving conflicts: +git add +git commit + +# View conflicts in a different style +git diff --ours # Your changes +git diff --theirs # Their changes +git diff --base # Original version +``` + +## Pro Tips + +💡 **Use `git diff` first** +Always compare branches before merging: +```bash +git diff main..feature-branch +``` + +💡 **Prevent conflicts** +- Pull changes frequently +- Communicate with your team about who's working on what +- Keep branches short-lived and merge often + +💡 **Make conflicts easier** +- Work on different files when possible +- Make small, focused commits +- If editing the same file, coordinate with teammates + +💡 **When stuck** +- Read the conflict markers carefully +- Look at `git log` to understand what each side changed +- Use `git diff` to see the changes +- Ask a teammate to review your resolution +- Use a merge tool: `git mergetool` + +## Merge Tools + +Git supports visual merge tools that make resolving conflicts easier: + +```bash +# Configure a merge tool (one-time setup) +git config --global merge.tool vscode # or meld, kdiff3, etc. + +# Use the merge tool during a conflict +git mergetool +``` + +This opens a visual interface showing both versions side-by-side. + +## Real-World Scenario + +This exercise simulates a common real-world situation: + +**Scenario:** Two developers working on the same file +- Alice adds a timeout configuration +- Bob adds debug mode configuration +- Both push their changes +- When Bob tries to merge, he gets a conflict +- Bob resolves it by keeping both changes +- Everyone's work is preserved! + +This happens all the time in team development. Conflicts are normal! + +## What You've Learned + +After completing this module, you understand: + +- ✅ Merge conflicts happen when the same lines are changed differently +- ✅ `git diff` helps you discover changes before merging +- ✅ Conflict markers show both versions +- ✅ You decide what the final code should look like +- ✅ Remove all markers before committing +- ✅ Test your resolution to ensure it works +- ✅ Conflicts are normal and easy to resolve with practice + +## Next Steps + +Ready to continue? The next module covers **cherry-picking** - selectively applying specific commits from one branch to another. + +To start over: +```bash +.\reset.ps1 +.\setup.ps1 +``` + +**Need help?** Review the steps above, or run `git status` to see what Git suggests! diff --git a/01-essentials/04-merge-conflict/challenge b/01-essentials/04-merge-conflict/challenge new file mode 160000 index 0000000..676b08e --- /dev/null +++ b/01-essentials/04-merge-conflict/challenge @@ -0,0 +1 @@ +Subproject commit 676b08e650cacba9c8fabee80697ca968bc4f3ea diff --git a/01-essentials/04-merge-conflict/reset.ps1 b/01-essentials/04-merge-conflict/reset.ps1 new file mode 100644 index 0000000..1013842 --- /dev/null +++ b/01-essentials/04-merge-conflict/reset.ps1 @@ -0,0 +1,23 @@ +#!/usr/bin/env pwsh +<# +.SYNOPSIS + Resets the Module 04 challenge environment. + +.DESCRIPTION + This script removes the challenge directory, allowing you to start fresh. + Run setup.ps1 again after resetting to recreate the environment. +#> + +Write-Host "`n=== Resetting Module 04 Challenge ===" -ForegroundColor Cyan + +if (Test-Path "challenge") { + Write-Host "Removing challenge directory..." -ForegroundColor Yellow + Remove-Item -Recurse -Force "challenge" + Write-Host "`n[SUCCESS] Challenge environment reset complete!" -ForegroundColor Green + Write-Host "`nRun .\setup.ps1 to create a fresh challenge environment." -ForegroundColor Cyan + Write-Host "" +} else { + Write-Host "`n[INFO] No challenge directory found. Nothing to reset." -ForegroundColor Yellow + Write-Host "Run .\setup.ps1 to create the challenge environment." -ForegroundColor Cyan + Write-Host "" +} diff --git a/01-essentials/04-merge-conflict/setup.ps1 b/01-essentials/04-merge-conflict/setup.ps1 new file mode 100644 index 0000000..86fa42d --- /dev/null +++ b/01-essentials/04-merge-conflict/setup.ps1 @@ -0,0 +1,125 @@ +#!/usr/bin/env pwsh +<# +.SYNOPSIS + Sets up the Module 04 challenge environment for merge conflicts. + +.DESCRIPTION + This script creates a challenge directory with a Git repository containing + two feature branches that have conflicting changes to the same file. + Students will learn to identify, understand, and resolve merge conflicts. +#> + +Write-Host "`n=== Setting up Module 04: Merge Conflicts ===" -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" + +# ============================================================================ +# Create base project +# ============================================================================ +Write-Host "Creating base project..." -ForegroundColor Cyan + +# Initial commit with config file +$configContent = @" +{ + "app": { + "name": "MyApp", + "version": "1.0.0", + "port": 3000 + } +} +"@ +Set-Content -Path "config.json" -Value $configContent +git add . +git commit -m "Initial commit with config" | Out-Null + +# Add README +$readmeContent = @" +# My Application + +A simple application for learning merge conflicts. + +## Configuration + +Edit `config.json` to configure the application. +"@ +Set-Content -Path "README.md" -Value $readmeContent +git add . +git commit -m "Add README" | Out-Null + +# ============================================================================ +# Branch 1: add-timeout (adds timeout setting) +# ============================================================================ +Write-Host "Creating add-timeout branch..." -ForegroundColor Cyan +git switch -c add-timeout | Out-Null + +$timeoutConfig = @" +{ + "app": { + "name": "MyApp", + "version": "1.0.0", + "port": 3000, + "timeout": 5000 + } +} +"@ +Set-Content -Path "config.json" -Value $timeoutConfig +git add . +git commit -m "Add timeout configuration" | Out-Null + +# ============================================================================ +# Branch 2: add-debug (adds debug setting - CONFLICTS with timeout!) +# ============================================================================ +Write-Host "Creating add-debug branch..." -ForegroundColor Cyan +git switch main | Out-Null +git switch -c add-debug | Out-Null + +$debugConfig = @" +{ + "app": { + "name": "MyApp", + "version": "1.0.0", + "port": 3000, + "debug": true + } +} +"@ +Set-Content -Path "config.json" -Value $debugConfig +git add . +git commit -m "Add debug mode configuration" | 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 "`nThe repository contains:" -ForegroundColor Yellow +Write-Host " - main branch: base configuration" -ForegroundColor White +Write-Host " - add-timeout branch: adds timeout setting" -ForegroundColor White +Write-Host " - add-debug branch: adds debug setting" -ForegroundColor White +Write-Host "`nBoth branches modify the same part of config.json!" -ForegroundColor Red +Write-Host "This will cause a merge conflict when you try to merge both." -ForegroundColor Red +Write-Host "`nNext steps:" -ForegroundColor Cyan +Write-Host " 1. Read the README.md for detailed instructions" -ForegroundColor White +Write-Host " 2. cd challenge" -ForegroundColor White +Write-Host " 3. Follow the guide to discover and resolve the conflict" -ForegroundColor White +Write-Host "" diff --git a/01-essentials/04-merge-conflict/verify.ps1 b/01-essentials/04-merge-conflict/verify.ps1 new file mode 100644 index 0000000..8a04114 --- /dev/null +++ b/01-essentials/04-merge-conflict/verify.ps1 @@ -0,0 +1,208 @@ +#!/usr/bin/env pwsh +<# +.SYNOPSIS + Verifies the Module 04 challenge solution. + +.DESCRIPTION + This script checks that you've successfully resolved the merge conflict by: + - Merging both branches into main + - Resolving the conflict in config.json + - Keeping both timeout and debug settings + - Ensuring valid JSON syntax +#> + +$script:allChecksPassed = $true + +# ============================================================================ +# Helper Functions +# ============================================================================ + +function Write-Pass { + param([string]$Message) + Write-Host "[PASS] $Message" -ForegroundColor Green +} + +function Write-Fail { + param([string]$Message) + Write-Host "[FAIL] $Message" -ForegroundColor Red + $script:allChecksPassed = $false +} + +function Write-Hint { + param([string]$Message) + Write-Host "[HINT] $Message" -ForegroundColor Yellow +} + +function Write-Info { + param([string]$Message) + Write-Host "[INFO] $Message" -ForegroundColor Cyan +} + +# ============================================================================ +# Check challenge directory exists +# ============================================================================ + +if (-not (Test-Path "challenge")) { + Write-Host "[ERROR] Challenge directory not found." -ForegroundColor Red + Write-Host "Run .\setup.ps1 first to create the challenge environment." -ForegroundColor Yellow + exit 1 +} + +Push-Location "challenge" + +if (-not (Test-Path ".git")) { + Write-Host "[ERROR] Not a git repository." -ForegroundColor Red + Write-Host "Run ..\setup.ps1 first to create the challenge environment." -ForegroundColor Yellow + Pop-Location + exit 1 +} + +Write-Host "`n=== Verifying Module 04: Merge Conflicts ===" -ForegroundColor Cyan + +# ============================================================================ +# Check current branch +# ============================================================================ +$currentBranch = git branch --show-current 2>$null +if ($currentBranch -eq "main") { + Write-Pass "Currently on main branch" +} else { + Write-Fail "Should be on main branch (currently on: $currentBranch)" + Write-Hint "Switch to main with: git switch main" +} + +# ============================================================================ +# Check that merge is not in progress +# ============================================================================ +if (Test-Path ".git/MERGE_HEAD") { + Write-Fail "Merge is still in progress (conflicts not resolved)" + Write-Hint "Resolve conflicts in config.json, then: git add config.json && git commit" + Pop-Location + exit 1 +} else { + Write-Pass "No merge in progress (conflicts resolved)" +} + +# ============================================================================ +# Check if config.json exists +# ============================================================================ +if (-not (Test-Path "config.json")) { + Write-Fail "File 'config.json' not found" + Write-Hint "The config.json file should exist" + Pop-Location + exit 1 +} + +# ============================================================================ +# Verify config.json is valid JSON +# ============================================================================ +try { + $configContent = Get-Content "config.json" -Raw + $config = $configContent | ConvertFrom-Json -ErrorAction Stop + Write-Pass "File 'config.json' is valid JSON" +} catch { + Write-Fail "File 'config.json' is not valid JSON" + Write-Hint "Make sure you removed all conflict markers (<<<<<<<, =======, >>>>>>>)" + Write-Hint "Check for missing commas or brackets" + Pop-Location + exit 1 +} + +# ============================================================================ +# Check for conflict markers +# ============================================================================ +if ($configContent -match '<<<<<<<|=======|>>>>>>>') { + Write-Fail "Conflict markers still present in config.json" + Write-Hint "Remove all conflict markers (<<<<<<<, =======, >>>>>>>)" + Pop-Location + exit 1 +} else { + Write-Pass "No conflict markers in config.json" +} + +# ============================================================================ +# Verify both settings are present (timeout and debug) +# ============================================================================ +if ($config.app.timeout -eq 5000) { + Write-Pass "Timeout setting preserved (5000)" +} else { + Write-Fail "Timeout setting missing or incorrect" + Write-Hint "Keep the timeout: 5000 setting from add-timeout branch" +} + +if ($config.app.debug -eq $true) { + Write-Pass "Debug setting preserved (true)" +} else { + Write-Fail "Debug setting missing or incorrect" + Write-Hint "Keep the debug: true setting from add-debug branch" +} + +# ============================================================================ +# Verify both branches were merged +# ============================================================================ +$addTimeoutMerged = git log --oneline --grep="add-timeout" 2>$null | Select-String "Merge" +$addDebugMerged = git log --oneline --grep="add-debug" 2>$null | Select-String "Merge" + +if ($addTimeoutMerged) { + Write-Pass "add-timeout branch has been merged" +} else { + # Check if it was a fast-forward merge (commits exist but no merge commit) + $timeoutCommit = git log --oneline --grep="Add timeout configuration" 2>$null + if ($timeoutCommit) { + Write-Pass "add-timeout branch changes are in main" + } else { + Write-Fail "add-timeout branch not merged" + Write-Hint "Merge with: git merge add-timeout" + } +} + +if ($addDebugMerged) { + Write-Pass "add-debug branch has been merged" +} else { + Write-Fail "add-debug branch not merged" + Write-Hint "Merge with: git merge add-debug" +} + +# ============================================================================ +# Check commit count (should have both merges) +# ============================================================================ +$totalCommits = [int](git rev-list --count HEAD 2>$null) +if ($totalCommits -ge 5) { + Write-Pass "Repository has $totalCommits commits (all merges complete)" +} else { + Write-Info "Repository has $totalCommits commits" + Write-Hint "Make sure both branches are merged" +} + +Pop-Location + +# ============================================================================ +# Final summary +# ============================================================================ +Write-Host "" +if ($script:allChecksPassed) { + Write-Host "=========================================" -ForegroundColor Green + Write-Host " CONGRATULATIONS! CHALLENGE PASSED!" -ForegroundColor Green + Write-Host "=========================================" -ForegroundColor Green + Write-Host "`nYou've successfully:" -ForegroundColor Cyan + Write-Host " ✓ Discovered changes using git diff" -ForegroundColor White + Write-Host " ✓ Merged the first branch" -ForegroundColor White + Write-Host " ✓ Encountered a merge conflict" -ForegroundColor White + Write-Host " ✓ Resolved the conflict by keeping both changes" -ForegroundColor White + Write-Host " ✓ Completed the merge" -ForegroundColor White + Write-Host "`nYou're now ready to handle merge conflicts in real projects!" -ForegroundColor Green + Write-Host "" + exit 0 +} else { + Write-Host "[SUMMARY] Some checks failed. Review the hints above." -ForegroundColor Red + Write-Host "" + Write-Host "Quick guide:" -ForegroundColor Cyan + Write-Host " 1. Make sure you're on main: git switch main" -ForegroundColor White + Write-Host " 2. Merge first branch: git merge add-timeout" -ForegroundColor White + Write-Host " 3. Merge second branch: git merge add-debug" -ForegroundColor White + Write-Host " 4. Resolve conflict: edit config.json, remove markers, keep both settings" -ForegroundColor White + Write-Host " 5. Stage resolved file: git add config.json" -ForegroundColor White + Write-Host " 6. Complete merge: git commit" -ForegroundColor White + Write-Host " 7. Run this verify script again" -ForegroundColor White + Write-Host "" + exit 1 +} diff --git a/01-essentials/04-cherry-pick/README.md b/01-essentials/05-cherry-pick/README.md similarity index 100% rename from 01-essentials/04-cherry-pick/README.md rename to 01-essentials/05-cherry-pick/README.md diff --git a/01-essentials/04-cherry-pick/reset.ps1 b/01-essentials/05-cherry-pick/reset.ps1 similarity index 100% rename from 01-essentials/04-cherry-pick/reset.ps1 rename to 01-essentials/05-cherry-pick/reset.ps1 diff --git a/01-essentials/04-cherry-pick/setup.ps1 b/01-essentials/05-cherry-pick/setup.ps1 similarity index 100% rename from 01-essentials/04-cherry-pick/setup.ps1 rename to 01-essentials/05-cherry-pick/setup.ps1 diff --git a/01-essentials/04-cherry-pick/verify.ps1 b/01-essentials/05-cherry-pick/verify.ps1 similarity index 100% rename from 01-essentials/04-cherry-pick/verify.ps1 rename to 01-essentials/05-cherry-pick/verify.ps1 diff --git a/01-essentials/05-revert/README.md b/01-essentials/06-revert/README.md similarity index 100% rename from 01-essentials/05-revert/README.md rename to 01-essentials/06-revert/README.md diff --git a/01-essentials/05-revert/reset.ps1 b/01-essentials/06-revert/reset.ps1 similarity index 100% rename from 01-essentials/05-revert/reset.ps1 rename to 01-essentials/06-revert/reset.ps1 diff --git a/01-essentials/05-revert/setup.ps1 b/01-essentials/06-revert/setup.ps1 similarity index 100% rename from 01-essentials/05-revert/setup.ps1 rename to 01-essentials/06-revert/setup.ps1 diff --git a/01-essentials/05-revert/verify.ps1 b/01-essentials/06-revert/verify.ps1 similarity index 100% rename from 01-essentials/05-revert/verify.ps1 rename to 01-essentials/06-revert/verify.ps1 diff --git a/01-essentials/06-reset/README.md b/01-essentials/07-reset/README.md similarity index 100% rename from 01-essentials/06-reset/README.md rename to 01-essentials/07-reset/README.md diff --git a/01-essentials/06-reset/reset.ps1 b/01-essentials/07-reset/reset.ps1 similarity index 100% rename from 01-essentials/06-reset/reset.ps1 rename to 01-essentials/07-reset/reset.ps1 diff --git a/01-essentials/06-reset/setup.ps1 b/01-essentials/07-reset/setup.ps1 similarity index 100% rename from 01-essentials/06-reset/setup.ps1 rename to 01-essentials/07-reset/setup.ps1 diff --git a/01-essentials/06-reset/verify.ps1 b/01-essentials/07-reset/verify.ps1 similarity index 100% rename from 01-essentials/06-reset/verify.ps1 rename to 01-essentials/07-reset/verify.ps1 diff --git a/01-essentials/07-stash/README.md b/01-essentials/08-stash/README.md similarity index 100% rename from 01-essentials/07-stash/README.md rename to 01-essentials/08-stash/README.md diff --git a/01-essentials/07-stash/reset.ps1 b/01-essentials/08-stash/reset.ps1 similarity index 100% rename from 01-essentials/07-stash/reset.ps1 rename to 01-essentials/08-stash/reset.ps1 diff --git a/01-essentials/07-stash/setup.ps1 b/01-essentials/08-stash/setup.ps1 similarity index 100% rename from 01-essentials/07-stash/setup.ps1 rename to 01-essentials/08-stash/setup.ps1 diff --git a/01-essentials/07-stash/verify.ps1 b/01-essentials/08-stash/verify.ps1 similarity index 100% rename from 01-essentials/07-stash/verify.ps1 rename to 01-essentials/08-stash/verify.ps1 diff --git a/01-essentials/08-multiplayer/01_FACILITATOR.md b/01-essentials/09-multiplayer/01_FACILITATOR.md similarity index 100% rename from 01-essentials/08-multiplayer/01_FACILITATOR.md rename to 01-essentials/09-multiplayer/01_FACILITATOR.md diff --git a/01-essentials/08-multiplayer/02_README.md b/01-essentials/09-multiplayer/02_README.md similarity index 100% rename from 01-essentials/08-multiplayer/02_README.md rename to 01-essentials/09-multiplayer/02_README.md diff --git a/01-essentials/08-multiplayer/03_TASKS.md b/01-essentials/09-multiplayer/03_TASKS.md similarity index 100% rename from 01-essentials/08-multiplayer/03_TASKS.md rename to 01-essentials/09-multiplayer/03_TASKS.md