feat: initial branching+merge+conflicts module merge
This commit is contained in:
839
01_essentials/03-branching-and-merging/README.md
Normal file
839
01_essentials/03-branching-and-merging/README.md
Normal file
@@ -0,0 +1,839 @@
|
||||
# Module 03: Branching and Merging
|
||||
|
||||
## About This Module
|
||||
|
||||
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:
|
||||
|
||||
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
|
||||
|
||||
Create the challenge environment:
|
||||
|
||||
```bash
|
||||
.\setup.ps1
|
||||
```
|
||||
|
||||
This creates a complete Git repository with all checkpoints ready.
|
||||
|
||||
### Working with Checkpoints
|
||||
|
||||
**View available checkpoints:**
|
||||
```bash
|
||||
.\reset.ps1
|
||||
```
|
||||
|
||||
**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!
|
||||
|
||||
### Why Use Branches?
|
||||
|
||||
Branches let you:
|
||||
- **Experiment safely** - Try new ideas without breaking main
|
||||
- **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
|
||||
|
||||
- **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
|
||||
|
||||
### 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 <branch-name>
|
||||
|
||||
# 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
|
||||
|
||||
```bash
|
||||
cd challenge
|
||||
git merge update-config
|
||||
```
|
||||
|
||||
**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 normal. Git is just asking for your help.
|
||||
|
||||
#### Step 2: Check What Happened
|
||||
|
||||
```bash
|
||||
git status
|
||||
```
|
||||
|
||||
**You'll see:**
|
||||
```
|
||||
On branch main
|
||||
You have unmerged paths.
|
||||
(fix conflicts and run "git commit")
|
||||
(use "git merge --abort" to abort the merge)
|
||||
|
||||
Unmerged paths:
|
||||
(use "git add <file>..." to mark resolution)
|
||||
both modified: config.json
|
||||
```
|
||||
|
||||
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:
|
||||
|
||||
```bash
|
||||
git add config.json
|
||||
```
|
||||
|
||||
#### Step 9: Check Status
|
||||
|
||||
```bash
|
||||
git status
|
||||
```
|
||||
|
||||
**You'll 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.
|
||||
|
||||
#### Step 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.
|
||||
|
||||
**Done!** Your merge is complete!
|
||||
|
||||
### 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:
|
||||
|
||||
```bash
|
||||
git merge --abort
|
||||
```
|
||||
|
||||
This returns your repository to the state before you started the merge. No harm done!
|
||||
|
||||
### Useful Commands
|
||||
|
||||
```bash
|
||||
# Attempt a merge
|
||||
git merge <branch-name>
|
||||
|
||||
# Check which files have conflicts
|
||||
git status
|
||||
|
||||
# Abort the merge and start over
|
||||
git merge --abort
|
||||
|
||||
# After resolving conflicts:
|
||||
git add <resolved-file>
|
||||
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
|
||||
|
||||
💡 **Prevent conflicts**
|
||||
- Pull changes frequently: `git pull`
|
||||
- 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
|
||||
- If you must edit the same file, coordinate with teammates
|
||||
- Make small, focused commits
|
||||
|
||||
💡 **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`
|
||||
|
||||
---
|
||||
|
||||
## Complete Command Reference
|
||||
|
||||
### 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
|
||||
```
|
||||
|
||||
### 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
|
||||
```
|
||||
|
||||
### 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
|
||||
```
|
||||
|
||||
### Conflict Resolution
|
||||
|
||||
```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
|
||||
```
|
||||
|
||||
### Checkpoint Commands (This Module)
|
||||
|
||||
```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
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### "I'm on the wrong branch!"
|
||||
|
||||
```bash
|
||||
git switch main # Switch to main
|
||||
git branch # Verify current branch
|
||||
```
|
||||
|
||||
### "I made commits on the wrong branch!"
|
||||
|
||||
Don't panic! You can move them:
|
||||
|
||||
```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!)
|
||||
```
|
||||
|
||||
Or use cherry-pick (covered in a later module).
|
||||
|
||||
### "The merge created a mess!"
|
||||
|
||||
Abort and try again:
|
||||
|
||||
```bash
|
||||
git merge --abort
|
||||
git status # Verify you're back to clean state
|
||||
```
|
||||
|
||||
### "I want to start this checkpoint over!"
|
||||
|
||||
Use the reset script:
|
||||
|
||||
```bash
|
||||
.\reset.ps1 start # Go back to Checkpoint 1
|
||||
```
|
||||
|
||||
This resets your repository to the beginning of that checkpoint.
|
||||
|
||||
### "I can't find my branch!"
|
||||
|
||||
List all branches:
|
||||
|
||||
```bash
|
||||
git branch --all # Shows all branches including remote
|
||||
```
|
||||
|
||||
The branch might have been deleted after merging (this is normal!).
|
||||
|
||||
### "How do I know which checkpoint I'm on?"
|
||||
|
||||
```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!**
|
||||
|
||||
---
|
||||
|
||||
## What You've Learned
|
||||
|
||||
By completing all three checkpoints, you now understand:
|
||||
|
||||
### Checkpoint 1: Branching Basics
|
||||
- ✅ Branches create independent lines of development
|
||||
- ✅ `git switch -c` creates and switches to 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
|
||||
- ✅ 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
|
||||
|
||||
---
|
||||
|
||||
## Next Steps
|
||||
|
||||
**Completed the module?** Great work! You're ready to move on.
|
||||
|
||||
**Want more practice?** Jump to any checkpoint and try again:
|
||||
```bash
|
||||
.\reset.ps1 start # Practice branching
|
||||
.\reset.ps1 merge # Practice merging
|
||||
.\reset.ps1 merge-conflict # Practice conflict resolution
|
||||
```
|
||||
|
||||
**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!
|
||||
1
01_essentials/03-branching-and-merging/challenge
Submodule
1
01_essentials/03-branching-and-merging/challenge
Submodule
Submodule 01_essentials/03-branching-and-merging/challenge added at 40d5f6c19c
216
01_essentials/03-branching-and-merging/reset.ps1
Executable file
216
01_essentials/03-branching-and-merging/reset.ps1
Executable file
@@ -0,0 +1,216 @@
|
||||
#!/usr/bin/env pwsh
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Resets the challenge environment to a specific checkpoint.
|
||||
|
||||
.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).
|
||||
#>
|
||||
|
||||
param(
|
||||
[ValidateSet('start', 'merge', 'merge-conflict', '')]
|
||||
[string]$Checkpoint = ''
|
||||
)
|
||||
|
||||
# Checkpoint to tag mapping
|
||||
$checkpointTags = @{
|
||||
'start' = 'checkpoint-start'
|
||||
'merge' = 'checkpoint-merge'
|
||||
'merge-conflict' = 'checkpoint-merge-conflict'
|
||||
}
|
||||
|
||||
# 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 <checkpoint>" -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
|
||||
279
01_essentials/03-branching-and-merging/setup.ps1
Executable file
279
01_essentials/03-branching-and-merging/setup.ps1
Executable file
@@ -0,0 +1,279 @@
|
||||
#!/usr/bin/env pwsh
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Sets up the Module 03 checkpoint-based challenge environment.
|
||||
|
||||
.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
|
||||
#>
|
||||
|
||||
Write-Host "`n=== Setting up Module 03: Branching and Merging ===" -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"
|
||||
|
||||
# ============================================================================
|
||||
# PHASE 1: Branching Basics - Initial commits on main
|
||||
# ============================================================================
|
||||
Write-Host "`nPhase 1: Creating initial project structure..." -ForegroundColor Cyan
|
||||
|
||||
# Commit 1: Initial commit
|
||||
$mainContent = @"
|
||||
# main.py - Main application file
|
||||
|
||||
def main():
|
||||
print("Welcome to the Application!")
|
||||
print("This is the main branch")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
"@
|
||||
Set-Content -Path "main.py" -Value $mainContent
|
||||
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
|
||||
|
||||
# ============================================================================
|
||||
# PHASE 2: Create feature-login branch (what students will do in checkpoint 1)
|
||||
# ============================================================================
|
||||
Write-Host "Phase 2: Creating feature-login branch..." -ForegroundColor Cyan
|
||||
|
||||
# Create and switch to feature-login branch
|
||||
git switch -c feature-login | Out-Null
|
||||
|
||||
# Commit 3: Add login module
|
||||
$loginContent = @"
|
||||
# login.py - User login module
|
||||
|
||||
def login(username, password):
|
||||
"""Authenticate a user."""
|
||||
print(f"Authenticating user: {username}")
|
||||
# TODO: Add actual authentication logic
|
||||
return True
|
||||
|
||||
def logout(username):
|
||||
"""Log out a user."""
|
||||
print(f"Logging out user: {username}")
|
||||
return True
|
||||
"@
|
||||
Set-Content -Path "login.py" -Value $loginContent
|
||||
git add .
|
||||
git commit -m "Add login module" | Out-Null
|
||||
|
||||
# Commit 4: Add password validation
|
||||
$loginContent = @"
|
||||
# login.py - User login module
|
||||
|
||||
def validate_password(password):
|
||||
"""Validate password strength."""
|
||||
if len(password) < 8:
|
||||
return False
|
||||
return True
|
||||
|
||||
def login(username, password):
|
||||
"""Authenticate a user."""
|
||||
if not validate_password(password):
|
||||
print("Password too weak!")
|
||||
return False
|
||||
print(f"Authenticating user: {username}")
|
||||
# TODO: Add actual authentication logic
|
||||
return True
|
||||
|
||||
def logout(username):
|
||||
"""Log out a user."""
|
||||
print(f"Logging out user: {username}")
|
||||
return True
|
||||
"@
|
||||
Set-Content -Path "login.py" -Value $loginContent
|
||||
git add .
|
||||
git commit -m "Add password validation" | Out-Null
|
||||
|
||||
# Switch back to main
|
||||
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
|
||||
|
||||
from main import main
|
||||
|
||||
def run():
|
||||
"""Run the application."""
|
||||
print("Starting application...")
|
||||
main()
|
||||
print("Application finished.")
|
||||
|
||||
if __name__ == "__main__":
|
||||
run()
|
||||
"@
|
||||
Set-Content -Path "app.py" -Value $appContent
|
||||
git add .
|
||||
git commit -m "Add app.py entry point" | Out-Null
|
||||
|
||||
# Commit 6: Add README
|
||||
$readmeContent = @"
|
||||
# My Application
|
||||
|
||||
Welcome to my application!
|
||||
|
||||
## Features
|
||||
|
||||
- Main functionality
|
||||
- More features coming soon
|
||||
|
||||
## Setup
|
||||
|
||||
Run: python app.py
|
||||
"@
|
||||
Set-Content -Path "README.md" -Value $readmeContent
|
||||
git add .
|
||||
git commit -m "Add README documentation" | Out-Null
|
||||
|
||||
# Tag checkpoint-merge (students begin merging here - divergent branches ready)
|
||||
Write-Host "Creating checkpoint: merge" -ForegroundColor Green
|
||||
git tag checkpoint-merge
|
||||
|
||||
# ============================================================================
|
||||
# PHASE 3: Merge feature-login into main (what students will do in checkpoint 2)
|
||||
# ============================================================================
|
||||
Write-Host "Phase 3: Merging feature-login into main..." -ForegroundColor Cyan
|
||||
|
||||
# Merge feature-login into main (will create three-way merge commit)
|
||||
git merge feature-login --no-edit | Out-Null
|
||||
|
||||
# ============================================================================
|
||||
# PHASE 4: Create conflict scenario (what students will do in checkpoint 3)
|
||||
# ============================================================================
|
||||
Write-Host "Phase 4: Creating merge conflict scenario..." -ForegroundColor Cyan
|
||||
|
||||
# Create config.json file on main
|
||||
$initialConfig = @"
|
||||
{
|
||||
"app": {
|
||||
"name": "MyApp",
|
||||
"version": "1.0.0",
|
||||
"port": 3000
|
||||
}
|
||||
}
|
||||
"@
|
||||
Set-Content -Path "config.json" -Value $initialConfig
|
||||
git add config.json
|
||||
git commit -m "Add initial configuration" | Out-Null
|
||||
|
||||
# On main branch: Add timeout setting
|
||||
$mainConfig = @"
|
||||
{
|
||||
"app": {
|
||||
"name": "MyApp",
|
||||
"version": "1.0.0",
|
||||
"port": 3000,
|
||||
"timeout": 5000
|
||||
}
|
||||
}
|
||||
"@
|
||||
Set-Content -Path "config.json" -Value $mainConfig
|
||||
git add config.json
|
||||
git commit -m "Add timeout configuration" | 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
|
||||
git switch main | 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
|
||||
|
||||
# ============================================================================
|
||||
# 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
|
||||
|
||||
# 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 "`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 ""
|
||||
320
01_essentials/03-branching-and-merging/verify.ps1
Executable file
320
01_essentials/03-branching-and-merging/verify.ps1
Executable file
@@ -0,0 +1,320 @@
|
||||
#!/usr/bin/env pwsh
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Verifies the Module 03 challenge solution (checkpoint-aware).
|
||||
|
||||
.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.
|
||||
#>
|
||||
|
||||
param(
|
||||
[ValidateSet('start', 'merge', 'merge-conflict', '')]
|
||||
[string]$Checkpoint = ''
|
||||
)
|
||||
|
||||
$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
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# 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
|
||||
}
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# Checkpoint 2: Merging Verification
|
||||
# ============================================================================
|
||||
|
||||
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
|
||||
exit 1
|
||||
}
|
||||
|
||||
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
|
||||
Pop-Location
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Run appropriate verification
|
||||
if ($Checkpoint -eq '') {
|
||||
# Verify all checkpoints
|
||||
Write-Host "`n=== Verifying All Checkpoints ===" -ForegroundColor Cyan
|
||||
|
||||
Verify-Branching
|
||||
Verify-Merging
|
||||
Verify-MergeConflicts
|
||||
|
||||
} else {
|
||||
# Verify specific checkpoint
|
||||
switch ($Checkpoint) {
|
||||
'start' { Verify-Branching }
|
||||
'merge' { Verify-Merging }
|
||||
'merge-conflict' { Verify-MergeConflicts }
|
||||
}
|
||||
}
|
||||
|
||||
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 ""
|
||||
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 ""
|
||||
exit 1
|
||||
}
|
||||
@@ -1,92 +0,0 @@
|
||||
# Module 03: Branching Basics
|
||||
|
||||
## Learning Objectives
|
||||
|
||||
In this module, you will:
|
||||
- Understand what a branch is in Git
|
||||
- Create new branches using `git branch` or `git switch -c`
|
||||
- Switch between branches using `git switch`
|
||||
- View all branches with `git branch`
|
||||
- Understand that branches allow parallel development
|
||||
|
||||
## Challenge
|
||||
|
||||
### Setup
|
||||
|
||||
Run the setup script to create your challenge environment:
|
||||
|
||||
```powershell
|
||||
.\setup.ps1
|
||||
```
|
||||
|
||||
This will create a `challenge/` directory with a Git repository that has some initial commits on the main branch.
|
||||
|
||||
### Your Task
|
||||
|
||||
Your goal is to create a feature branch, make commits on it, and understand how branches work independently.
|
||||
|
||||
**Steps:**
|
||||
|
||||
1. Create a new branch called `feature-login`
|
||||
2. Switch to the new branch
|
||||
3. Create a new file `login.py` with some login functionality
|
||||
4. Commit the new file to your feature branch
|
||||
5. Make another change to `login.py` and commit it
|
||||
6. Switch back to the main branch and observe that `login.py` doesn't exist there
|
||||
|
||||
**Suggested Approach:**
|
||||
|
||||
1. Navigate to the challenge directory: `cd challenge`
|
||||
2. View existing branches: `git branch`
|
||||
3. Create and switch to new branch: `git switch -c feature-login`
|
||||
4. Create `login.py` with any content you like
|
||||
5. Stage and commit: `git add login.py` and `git commit -m "Add login functionality"`
|
||||
6. Modify `login.py`, then commit again
|
||||
7. Switch back to main: `git switch main`
|
||||
8. Run `ls` (or check your file explorer) and notice that `login.py` doesn't exist on main!
|
||||
9. Switch back to feature-login: `git switch feature-login`
|
||||
10. Run `ls` (or check your file explorer) again and see that `login.py` is back!
|
||||
|
||||
> **Important Notes:**
|
||||
> - Use `git switch` to change branches
|
||||
> - `git switch -c <name>` creates and switches in one command
|
||||
> - Branches are independent - files in one branch don't affect another until you merge
|
||||
> - You can switch between branches as many times as you want
|
||||
|
||||
## Key Concepts
|
||||
|
||||
- **Branch**: A lightweight movable pointer to a commit. Branches allow you to work on different features independently.
|
||||
- **HEAD**: A pointer to the current branch you're working on. When you switch branches, HEAD moves.
|
||||
- **main/master**: The default branch name in Git (main is the modern convention, master is older).
|
||||
- **Feature Branch**: A branch created to develop a specific feature, separate from the main codebase.
|
||||
|
||||
## Useful Commands
|
||||
|
||||
```bash
|
||||
git branch # List all branches (* shows current)
|
||||
git branch <name> # Create a new branch
|
||||
git switch <branch> # Switch to an existing branch
|
||||
git switch -c <name> # Create and switch to new branch
|
||||
git switch - # Switch back to previous branch
|
||||
git branch -d <name> # Delete a branch (we won't use this yet)
|
||||
```
|
||||
|
||||
## Verification
|
||||
|
||||
Once you've completed the challenge, verify your solution:
|
||||
|
||||
```powershell
|
||||
.\verify.ps1
|
||||
```
|
||||
|
||||
The verification script will check that you've created the branch, made commits, and that the branches are independent.
|
||||
|
||||
## Need to Start Over?
|
||||
|
||||
If you want to reset the challenge and start fresh:
|
||||
|
||||
```powershell
|
||||
.\reset.ps1
|
||||
```
|
||||
|
||||
This will remove the challenge directory and run the setup script again, giving you a clean slate.
|
||||
@@ -1,26 +0,0 @@
|
||||
#!/usr/bin/env pwsh
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Resets the Module 03 challenge environment.
|
||||
|
||||
.DESCRIPTION
|
||||
This script removes the existing challenge directory and runs
|
||||
the setup script again to create a fresh challenge environment.
|
||||
#>
|
||||
|
||||
Write-Host "`n=== Resetting Module 03 Challenge ===" -ForegroundColor Cyan
|
||||
|
||||
# Remove existing challenge directory if it exists
|
||||
if (Test-Path "challenge") {
|
||||
Write-Host "Removing existing challenge directory..." -ForegroundColor Yellow
|
||||
Remove-Item -Recurse -Force "challenge"
|
||||
} else {
|
||||
Write-Host "No existing challenge directory found." -ForegroundColor Cyan
|
||||
}
|
||||
|
||||
Write-Host ""
|
||||
Write-Host "----------------------------------------" -ForegroundColor Cyan
|
||||
Write-Host ""
|
||||
|
||||
# Run setup script
|
||||
& "$PSScriptRoot\setup.ps1"
|
||||
@@ -1,85 +0,0 @@
|
||||
#!/usr/bin/env pwsh
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Sets up the Module 03 challenge environment for learning about branches.
|
||||
|
||||
.DESCRIPTION
|
||||
This script creates a challenge directory with a Git repository that
|
||||
contains a couple of commits on the main branch. Students will create
|
||||
a feature branch and make commits on it.
|
||||
#>
|
||||
|
||||
Write-Host "`n=== Setting up Module 03 Challenge ===" -ForegroundColor Cyan
|
||||
|
||||
# Remove existing challenge directory if it exists
|
||||
if (Test-Path "challenge") {
|
||||
Write-Host "Removing existing challenge directory..." -ForegroundColor Yellow
|
||||
Remove-Item -Recurse -Force "challenge"
|
||||
}
|
||||
|
||||
# Create fresh challenge directory
|
||||
Write-Host "Creating challenge directory..." -ForegroundColor Green
|
||||
New-Item -ItemType Directory -Path "challenge" | Out-Null
|
||||
Set-Location "challenge"
|
||||
|
||||
# Initialize Git repository
|
||||
Write-Host "Initializing Git repository..." -ForegroundColor Green
|
||||
git init | Out-Null
|
||||
|
||||
# Configure git for this repository
|
||||
git config user.name "Workshop Student"
|
||||
git config user.email "student@example.com"
|
||||
|
||||
# Commit 1: Initial commit
|
||||
Write-Host "Creating initial project..." -ForegroundColor Green
|
||||
$mainContent = @"
|
||||
# main.py - Main application file
|
||||
|
||||
def main():
|
||||
print("Welcome to the Application!")
|
||||
print("This is the main branch")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
"@
|
||||
Set-Content -Path "main.py" -Value $mainContent
|
||||
|
||||
git add .
|
||||
git commit -m "Initial commit" | Out-Null
|
||||
|
||||
# Commit 2: Add main functionality
|
||||
Write-Host "Adding main functionality..." -ForegroundColor Green
|
||||
$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
|
||||
|
||||
# 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 "`nNext steps:" -ForegroundColor Cyan
|
||||
Write-Host " 1. cd challenge" -ForegroundColor White
|
||||
Write-Host " 2. Create a new branch: git switch -c feature-login" -ForegroundColor White
|
||||
Write-Host " 3. Create login.py and commit it" -ForegroundColor White
|
||||
Write-Host " 4. Make another commit on the feature branch" -ForegroundColor White
|
||||
Write-Host " 5. Switch back to main: git switch main" -ForegroundColor White
|
||||
Write-Host " 6. Observe that login.py doesn't exist on main!" -ForegroundColor White
|
||||
Write-Host " 7. Run '..\verify.ps1' to check your solution" -ForegroundColor White
|
||||
Write-Host ""
|
||||
@@ -1,105 +0,0 @@
|
||||
#!/usr/bin/env pwsh
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Verifies the Module 03 challenge solution.
|
||||
|
||||
.DESCRIPTION
|
||||
This script checks that:
|
||||
- The challenge directory exists
|
||||
- A Git repository exists
|
||||
- The feature-login branch exists
|
||||
- The branch has at least 2 new commits
|
||||
- login.py exists in the feature branch but not in main
|
||||
#>
|
||||
|
||||
Write-Host "`n=== Verifying Module 03 Solution ===" -ForegroundColor Cyan
|
||||
|
||||
$allChecksPassed = $true
|
||||
|
||||
# Check if challenge directory exists
|
||||
if (-not (Test-Path "challenge")) {
|
||||
Write-Host "[FAIL] Challenge directory not found. Did you run setup.ps1?" -ForegroundColor Red
|
||||
exit 1
|
||||
}
|
||||
|
||||
Set-Location "challenge"
|
||||
|
||||
# Check if git repository exists
|
||||
if (-not (Test-Path ".git")) {
|
||||
Write-Host "[FAIL] Not a git repository. Did you run setup.ps1?" -ForegroundColor Red
|
||||
Set-Location ..
|
||||
exit 1
|
||||
}
|
||||
|
||||
# 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-Host "[PASS] Branch 'feature-login' exists" -ForegroundColor Green
|
||||
} else {
|
||||
Write-Host "[FAIL] Branch 'feature-login' not found" -ForegroundColor Red
|
||||
Write-Host "[HINT] Create the branch with: git switch -c feature-login" -ForegroundColor Yellow
|
||||
$allChecksPassed = $false
|
||||
Set-Location ..
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Check if feature-login has commits beyond main
|
||||
$commitCount = git rev-list main..feature-login --count 2>$null
|
||||
if ($commitCount -ge 2) {
|
||||
Write-Host "[PASS] Branch 'feature-login' has $commitCount new commits" -ForegroundColor Green
|
||||
} else {
|
||||
Write-Host "[FAIL] Branch 'feature-login' needs at least 2 new commits (found: $commitCount)" -ForegroundColor Red
|
||||
Write-Host "[HINT] Make sure you've committed login.py and made at least one more commit" -ForegroundColor Yellow
|
||||
$allChecksPassed = $false
|
||||
}
|
||||
|
||||
# Switch to feature-login and check for login.py
|
||||
git checkout feature-login 2>$null | Out-Null
|
||||
if (Test-Path "login.py") {
|
||||
Write-Host "[PASS] File 'login.py' exists in feature-login branch" -ForegroundColor Green
|
||||
} else {
|
||||
Write-Host "[FAIL] File 'login.py' not found in feature-login branch" -ForegroundColor Red
|
||||
Write-Host "[HINT] Create login.py and commit it to the feature-login branch" -ForegroundColor Yellow
|
||||
$allChecksPassed = $false
|
||||
}
|
||||
|
||||
# Switch to main and verify login.py doesn't exist
|
||||
git switch main 2>$null | Out-Null
|
||||
if (-not (Test-Path "login.py")) {
|
||||
Write-Host "[PASS] File 'login.py' does NOT exist in main branch (branches are independent!)" -ForegroundColor Green
|
||||
} else {
|
||||
Write-Host "[FAIL] File 'login.py' should not exist in main branch" -ForegroundColor Red
|
||||
Write-Host "[HINT] Make sure you created login.py only on the feature-login branch" -ForegroundColor Yellow
|
||||
$allChecksPassed = $false
|
||||
}
|
||||
|
||||
# Switch back to original branch
|
||||
if ($originalBranch) {
|
||||
git switch $originalBranch 2>$null | Out-Null
|
||||
}
|
||||
|
||||
Set-Location ..
|
||||
|
||||
# Final summary
|
||||
if ($allChecksPassed) {
|
||||
Write-Host "`n" -NoNewline
|
||||
Write-Host "=====================================" -ForegroundColor Green
|
||||
Write-Host " CONGRATULATIONS! CHALLENGE PASSED!" -ForegroundColor Green
|
||||
Write-Host "=====================================" -ForegroundColor Green
|
||||
Write-Host "`nYou've successfully learned about Git branches!" -ForegroundColor Cyan
|
||||
Write-Host "You now understand:" -ForegroundColor Cyan
|
||||
Write-Host " - How to create branches with git switch -c" -ForegroundColor White
|
||||
Write-Host " - How to switch between branches" -ForegroundColor White
|
||||
Write-Host " - That branches are independent lines of development" -ForegroundColor White
|
||||
Write-Host " - That files in one branch don't affect another" -ForegroundColor White
|
||||
Write-Host "`nReady for the next module!" -ForegroundColor Green
|
||||
Write-Host ""
|
||||
} else {
|
||||
Write-Host "`n[SUMMARY] Some checks failed. Review the hints above and try again." -ForegroundColor Red
|
||||
Write-Host "[INFO] You can run this verification script as many times as needed." -ForegroundColor Yellow
|
||||
Write-Host ""
|
||||
exit 1
|
||||
}
|
||||
@@ -1,229 +0,0 @@
|
||||
# Module 04: Merging Branches
|
||||
|
||||
## Learning Objectives
|
||||
|
||||
In this module, you will:
|
||||
- Understand what merging means in Git
|
||||
- Merge a feature branch back into main
|
||||
- Use `git merge` to combine branches
|
||||
- Visualize merged branches with `git log --graph`
|
||||
|
||||
## Challenge
|
||||
|
||||
### Setup
|
||||
|
||||
Run the setup script to create your challenge environment:
|
||||
|
||||
```powershell
|
||||
.\setup.ps1
|
||||
```
|
||||
|
||||
This will create a `challenge/` directory with a Git repository that has a main branch and a feature branch.
|
||||
|
||||
### Your Task
|
||||
|
||||
You've been working on a new login feature in a separate branch. Now it's time to merge your work back into the main branch!
|
||||
|
||||
**Scenario:**
|
||||
- You created a `feature-login` branch to add login functionality
|
||||
- You made some commits on that branch
|
||||
- Meanwhile, your teammate updated the README on main
|
||||
- Now you need to bring your login feature back into main
|
||||
|
||||
**Steps:**
|
||||
|
||||
1. Navigate to the challenge directory: `cd challenge`
|
||||
2. Check which branch you're on: `git branch`
|
||||
3. View all branches: `git log --oneline --graph --all`
|
||||
4. Merge the feature-login branch into main: `git merge feature-login`
|
||||
5. View the result: `git log --oneline --graph --all`
|
||||
|
||||
> **That's it!** Merging is how you bring work from one branch into another.
|
||||
|
||||
## What is Merging?
|
||||
|
||||
**Merging** is the process of taking changes from one branch and bringing them into another branch.
|
||||
|
||||
Think of it like combining two streams into one river - all the water (code) flows together.
|
||||
|
||||
### Before Merging
|
||||
|
||||
You have two branches with different work:
|
||||
|
||||
```
|
||||
main: A---B---C
|
||||
\
|
||||
feature-login: D---E
|
||||
```
|
||||
|
||||
- Main branch has commits A, B, and C
|
||||
- Feature-login branch has commits D and E
|
||||
- They split apart at commit B
|
||||
|
||||
### After Merging
|
||||
|
||||
You bring the feature branch into main:
|
||||
|
||||
```
|
||||
main: A---B---C---M
|
||||
\ /
|
||||
feature-login: D-E
|
||||
```
|
||||
|
||||
- Commit M is a **merge commit** - it combines both branches
|
||||
- Main now has all the work from both branches
|
||||
- Your login feature is now part of main!
|
||||
|
||||
## How to Merge
|
||||
|
||||
Merging is simple - just two steps:
|
||||
|
||||
**1. Switch to the branch you want to merge INTO**
|
||||
```bash
|
||||
git switch main
|
||||
```
|
||||
This is the branch that will receive the changes.
|
||||
|
||||
**2. Merge the other branch**
|
||||
```bash
|
||||
git merge feature-login
|
||||
```
|
||||
This brings changes from feature-login into main.
|
||||
|
||||
**That's it!** Git does the work of combining the changes.
|
||||
|
||||
## Understanding the Merge Commit
|
||||
|
||||
When you merge, Git creates a special commit called a **merge commit**.
|
||||
|
||||
**What makes it special?**
|
||||
- It has TWO parent commits (one from each branch)
|
||||
- It represents the point where branches came back together
|
||||
- Git writes a message like "Merge branch 'feature-login'"
|
||||
|
||||
You can view the merge commit just like any other commit:
|
||||
```bash
|
||||
git show HEAD
|
||||
```
|
||||
|
||||
## Visualizing Merges
|
||||
|
||||
Use `git log --graph` to see how branches merged:
|
||||
|
||||
```bash
|
||||
git log --oneline --graph --all
|
||||
```
|
||||
|
||||
**Example output:**
|
||||
```
|
||||
* a1b2c3d (HEAD -> main) Merge branch 'feature-login'
|
||||
|\
|
||||
| * e5f6g7h (feature-login) Implement login validation
|
||||
| * h8i9j0k Add login form
|
||||
* | k1l2m3n Update README with setup instructions
|
||||
|/
|
||||
* n4o5p6q Initial project structure
|
||||
```
|
||||
|
||||
**Reading the graph:**
|
||||
- `*` = A commit
|
||||
- `|` = Branch line
|
||||
- `/` and `\` = Branches splitting or joining
|
||||
- `(HEAD -> main)` = You are here
|
||||
|
||||
The graph shows how the branches split and came back together!
|
||||
|
||||
## Useful Commands
|
||||
|
||||
```bash
|
||||
# Merging
|
||||
git merge <branch-name> # Merge a branch into current branch
|
||||
|
||||
# Viewing
|
||||
git log --oneline --graph # See branch history visually
|
||||
git log --oneline --graph --all # Include all branches
|
||||
|
||||
# Branch management
|
||||
git branch # List branches
|
||||
git switch <branch-name> # Switch to a branch
|
||||
git branch -d <branch-name> # Delete a branch (after merging)
|
||||
```
|
||||
|
||||
## Common Questions
|
||||
|
||||
### "What if I'm on the wrong branch when I merge?"
|
||||
|
||||
Don't worry! The branch you're currently on is the one that receives the changes.
|
||||
|
||||
**Example:**
|
||||
```bash
|
||||
git switch main # Go to main
|
||||
git merge feature-login # Bring feature-login INTO main
|
||||
```
|
||||
|
||||
Always switch to the destination branch first!
|
||||
|
||||
### "Can I undo a merge?"
|
||||
|
||||
Yes! Before you push to a remote:
|
||||
```bash
|
||||
git reset --hard HEAD~1
|
||||
```
|
||||
|
||||
This removes the merge commit. (We'll cover this more in later modules)
|
||||
|
||||
### "What happens to the feature branch after merging?"
|
||||
|
||||
The feature branch still exists! The merge just copies its commits into main.
|
||||
|
||||
You can delete it if you're done:
|
||||
```bash
|
||||
git branch -d feature-login # Safe delete (only if merged)
|
||||
```
|
||||
|
||||
The commits are still in history - you're just removing the branch label.
|
||||
|
||||
### "What if Git can't merge automatically?"
|
||||
|
||||
Sometimes Git needs your help when the same lines were changed in both branches. This is called a **merge conflict**.
|
||||
|
||||
Don't worry - we'll learn how to handle conflicts in the next module!
|
||||
|
||||
If you encounter a conflict now and want to cancel:
|
||||
```bash
|
||||
git merge --abort
|
||||
```
|
||||
|
||||
## Verification
|
||||
|
||||
Once you've merged the feature-login branch, verify your solution:
|
||||
|
||||
```powershell
|
||||
.\verify.ps1
|
||||
```
|
||||
|
||||
The verification script will check that you've successfully merged.
|
||||
|
||||
## Need to Start Over?
|
||||
|
||||
If you want to reset the challenge and start fresh:
|
||||
|
||||
```powershell
|
||||
.\reset.ps1
|
||||
```
|
||||
|
||||
## What's Next?
|
||||
|
||||
**Next module:** Merge Conflicts - Learn what to do when Git can't automatically merge changes.
|
||||
|
||||
**Later:** In the advanced modules, you'll learn about different merging strategies and when to use them. For now, understanding basic merging is all you need!
|
||||
|
||||
## Quick Summary
|
||||
|
||||
✅ **Merging** combines work from one branch into another
|
||||
✅ Switch to the destination branch, then run `git merge <source-branch>`
|
||||
✅ Git creates a **merge commit** to record the merge
|
||||
✅ Use `git log --graph` to visualize how branches merged
|
||||
✅ The feature branch still exists after merging - you can delete it if you want
|
||||
|
||||
That's all there is to basic merging! 🎉
|
||||
@@ -1,26 +0,0 @@
|
||||
#!/usr/bin/env pwsh
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Resets the Module 04 challenge environment.
|
||||
|
||||
.DESCRIPTION
|
||||
This script removes the existing challenge directory and runs
|
||||
the setup script again to create a fresh challenge environment.
|
||||
#>
|
||||
|
||||
Write-Host "`n=== Resetting Module 04 Challenge ===" -ForegroundColor Cyan
|
||||
|
||||
# Remove existing challenge directory if it exists
|
||||
if (Test-Path "challenge") {
|
||||
Write-Host "Removing existing challenge directory..." -ForegroundColor Yellow
|
||||
Remove-Item -Recurse -Force "challenge"
|
||||
} else {
|
||||
Write-Host "No existing challenge directory found." -ForegroundColor Cyan
|
||||
}
|
||||
|
||||
Write-Host ""
|
||||
Write-Host "----------------------------------------" -ForegroundColor Cyan
|
||||
Write-Host ""
|
||||
|
||||
# Run setup script
|
||||
& "$PSScriptRoot\setup.ps1"
|
||||
@@ -1,246 +0,0 @@
|
||||
#!/usr/bin/env pwsh
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Sets up the Module 04 challenge environment for learning about merging.
|
||||
|
||||
.DESCRIPTION
|
||||
This script creates a challenge directory with a Git repository that
|
||||
contains two divergent branches ready to merge (three-way merge scenario).
|
||||
#>
|
||||
|
||||
Write-Host "`n=== Setting up Module 04 Challenge ===" -ForegroundColor Cyan
|
||||
|
||||
# Remove existing challenge directory if it exists
|
||||
if (Test-Path "challenge") {
|
||||
Write-Host "Removing existing challenge directory..." -ForegroundColor Yellow
|
||||
Remove-Item -Recurse -Force "challenge"
|
||||
}
|
||||
|
||||
# Create fresh challenge directory
|
||||
Write-Host "Creating challenge directory..." -ForegroundColor Green
|
||||
New-Item -ItemType Directory -Path "challenge" | Out-Null
|
||||
Set-Location "challenge"
|
||||
|
||||
# Initialize Git repository
|
||||
Write-Host "Initializing Git repository..." -ForegroundColor Green
|
||||
git init | Out-Null
|
||||
|
||||
# Configure git for this repository
|
||||
git config user.name "Workshop Student"
|
||||
git config user.email "student@example.com"
|
||||
|
||||
# Commit 1: Initial project structure on main
|
||||
Write-Host "Creating initial project structure..." -ForegroundColor Green
|
||||
$readmeContent = @"
|
||||
# My Project
|
||||
|
||||
A simple web application project.
|
||||
|
||||
## Setup
|
||||
|
||||
Coming soon...
|
||||
"@
|
||||
Set-Content -Path "README.md" -Value $readmeContent
|
||||
|
||||
$appContent = @"
|
||||
# app.py - Main application file
|
||||
|
||||
def main():
|
||||
print("Welcome to My App!")
|
||||
pass
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
"@
|
||||
Set-Content -Path "app.py" -Value $appContent
|
||||
|
||||
git add .
|
||||
git commit -m "Initial project structure" | Out-Null
|
||||
|
||||
# Commit 2: Add configuration file on main
|
||||
Write-Host "Adding configuration..." -ForegroundColor Green
|
||||
$configContent = @"
|
||||
# config.py - Configuration settings
|
||||
|
||||
APP_NAME = "My Project"
|
||||
VERSION = "1.0.0"
|
||||
DEBUG = False
|
||||
"@
|
||||
Set-Content -Path "config.py" -Value $configContent
|
||||
|
||||
git add .
|
||||
git commit -m "Add basic configuration" | Out-Null
|
||||
|
||||
# Commit 3: Add database utilities on main
|
||||
Write-Host "Adding database utilities..." -ForegroundColor Green
|
||||
$dbContent = @"
|
||||
# database.py - Database utilities
|
||||
|
||||
def connect():
|
||||
"""Connect to database."""
|
||||
print("Connecting to database...")
|
||||
return True
|
||||
|
||||
def disconnect():
|
||||
"""Disconnect from database."""
|
||||
print("Disconnecting...")
|
||||
return True
|
||||
"@
|
||||
Set-Content -Path "database.py" -Value $dbContent
|
||||
|
||||
git add .
|
||||
git commit -m "Add database utilities" | Out-Null
|
||||
|
||||
# Create feature-login branch
|
||||
Write-Host "Creating feature-login branch..." -ForegroundColor Green
|
||||
git switch -c feature-login | Out-Null
|
||||
|
||||
# Commit 4: Add login module on feature-login
|
||||
Write-Host "Adding login functionality on feature-login..." -ForegroundColor Green
|
||||
$loginContent = @"
|
||||
# login.py - User login module
|
||||
|
||||
def login(username, password):
|
||||
"""Authenticate a user."""
|
||||
print(f"Authenticating user: {username}")
|
||||
# TODO: Add actual authentication logic
|
||||
return True
|
||||
|
||||
def logout(username):
|
||||
"""Log out a user."""
|
||||
print(f"Logging out user: {username}")
|
||||
return True
|
||||
"@
|
||||
Set-Content -Path "login.py" -Value $loginContent
|
||||
|
||||
git add .
|
||||
git commit -m "Add login module" | Out-Null
|
||||
|
||||
# Commit 5: Add password validation on feature-login
|
||||
$loginContent = @"
|
||||
# login.py - User login module
|
||||
|
||||
def validate_password(password):
|
||||
"""Validate password strength."""
|
||||
if len(password) < 8:
|
||||
return False
|
||||
return True
|
||||
|
||||
def login(username, password):
|
||||
"""Authenticate a user."""
|
||||
if not validate_password(password):
|
||||
print("Password too weak!")
|
||||
return False
|
||||
print(f"Authenticating user: {username}")
|
||||
# TODO: Add actual authentication logic
|
||||
return True
|
||||
|
||||
def logout(username):
|
||||
"""Log out a user."""
|
||||
print(f"Logging out user: {username}")
|
||||
return True
|
||||
"@
|
||||
Set-Content -Path "login.py" -Value $loginContent
|
||||
|
||||
git add .
|
||||
git commit -m "Add password validation" | Out-Null
|
||||
|
||||
# Commit 6: Integrate login with app on feature-login
|
||||
$appContent = @"
|
||||
# app.py - Main application file
|
||||
from login import login, logout
|
||||
|
||||
def main():
|
||||
print("Welcome to My App!")
|
||||
# Add login integration
|
||||
if login("testuser", "password123"):
|
||||
print("Login successful!")
|
||||
pass
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
"@
|
||||
Set-Content -Path "app.py" -Value $appContent
|
||||
|
||||
git add .
|
||||
git commit -m "Integrate login with main app" | Out-Null
|
||||
|
||||
# Commit 7: Add session management on feature-login
|
||||
$sessionContent = @"
|
||||
# session.py - Session management
|
||||
|
||||
class Session:
|
||||
def __init__(self, username):
|
||||
self.username = username
|
||||
self.active = True
|
||||
|
||||
def end(self):
|
||||
"""End the session."""
|
||||
self.active = False
|
||||
print(f"Session ended for {self.username}")
|
||||
"@
|
||||
Set-Content -Path "session.py" -Value $sessionContent
|
||||
|
||||
git add .
|
||||
git commit -m "Add session management" | Out-Null
|
||||
|
||||
# Switch back to main branch
|
||||
Write-Host "Switching back to main branch..." -ForegroundColor Green
|
||||
git switch main | Out-Null
|
||||
|
||||
# Commit 8: Update README on main (creates divergence)
|
||||
Write-Host "Adding documentation on main (creates divergence)..." -ForegroundColor Green
|
||||
$readmeContent = @"
|
||||
# My Project
|
||||
|
||||
A simple web application project.
|
||||
|
||||
## Setup
|
||||
|
||||
1. Install Python 3.8 or higher
|
||||
2. Run: python app.py
|
||||
|
||||
## Features
|
||||
|
||||
- User authentication (coming soon)
|
||||
- Data management (coming soon)
|
||||
|
||||
## Contributing
|
||||
|
||||
Please follow our coding standards when contributing.
|
||||
"@
|
||||
Set-Content -Path "README.md" -Value $readmeContent
|
||||
|
||||
git add .
|
||||
git commit -m "Update README with setup instructions" | Out-Null
|
||||
|
||||
# Commit 9: Update configuration on main
|
||||
$configContent = @"
|
||||
# config.py - Configuration settings
|
||||
|
||||
APP_NAME = "My Project"
|
||||
VERSION = "1.0.0"
|
||||
DEBUG = False
|
||||
DATABASE_PATH = "./data/app.db"
|
||||
LOG_LEVEL = "INFO"
|
||||
"@
|
||||
Set-Content -Path "config.py" -Value $configContent
|
||||
|
||||
git add .
|
||||
git commit -m "Add database path to config" | Out-Null
|
||||
|
||||
# Return to module directory
|
||||
Set-Location ..
|
||||
|
||||
Write-Host "`n=== Setup Complete! ===" -ForegroundColor Green
|
||||
Write-Host "`nYour challenge environment is ready in the 'challenge/' directory." -ForegroundColor Cyan
|
||||
Write-Host "`nScenario: You have two divergent branches!" -ForegroundColor Yellow
|
||||
Write-Host " - main: Has 5 commits (config, database utils, README updates)" -ForegroundColor White
|
||||
Write-Host " - feature-login: Has 4 commits (login, validation, session)" -ForegroundColor White
|
||||
Write-Host "`nNext steps:" -ForegroundColor Cyan
|
||||
Write-Host " 1. cd challenge" -ForegroundColor White
|
||||
Write-Host " 2. View the branch structure: git log --oneline --graph --all" -ForegroundColor White
|
||||
Write-Host " 3. Merge feature-login into main: git merge feature-login" -ForegroundColor White
|
||||
Write-Host " 4. View the merge result: git log --oneline --graph --all" -ForegroundColor White
|
||||
Write-Host " 5. Run '..\verify.ps1' to check your solution" -ForegroundColor White
|
||||
Write-Host ""
|
||||
@@ -1,122 +0,0 @@
|
||||
#!/usr/bin/env pwsh
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Verifies the Module 04 challenge solution.
|
||||
|
||||
.DESCRIPTION
|
||||
This script checks that:
|
||||
- The challenge directory exists
|
||||
- A Git repository exists
|
||||
- Currently on main branch
|
||||
- feature-login has been merged into main
|
||||
- A merge commit exists (three-way merge)
|
||||
- Login functionality is present on main
|
||||
#>
|
||||
|
||||
Write-Host "`n=== Verifying Module 04 Solution ===" -ForegroundColor Cyan
|
||||
|
||||
$allChecksPassed = $true
|
||||
|
||||
# Check if challenge directory exists
|
||||
if (-not (Test-Path "challenge")) {
|
||||
Write-Host "[FAIL] Challenge directory not found. Did you run setup.ps1?" -ForegroundColor Red
|
||||
exit 1
|
||||
}
|
||||
|
||||
Set-Location "challenge"
|
||||
|
||||
# Check if git repository exists
|
||||
if (-not (Test-Path ".git")) {
|
||||
Write-Host "[FAIL] Not a git repository. Did you run setup.ps1?" -ForegroundColor Red
|
||||
Set-Location ..
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Check current branch is main
|
||||
$currentBranch = git branch --show-current 2>$null
|
||||
if ($currentBranch -eq "main") {
|
||||
Write-Host "[PASS] Currently on main branch" -ForegroundColor Green
|
||||
} else {
|
||||
Write-Host "[FAIL] Not on main branch (currently on: $currentBranch)" -ForegroundColor Red
|
||||
Write-Host "[HINT] Switch to main with: git switch main" -ForegroundColor Yellow
|
||||
$allChecksPassed = $false
|
||||
}
|
||||
|
||||
# Check if feature-login branch exists
|
||||
$featureLoginBranch = git branch --list "feature-login" 2>$null
|
||||
if ($featureLoginBranch) {
|
||||
Write-Host "[PASS] Branch 'feature-login' exists" -ForegroundColor Green
|
||||
} else {
|
||||
Write-Host "[INFO] Branch 'feature-login' not found (may have been deleted after merge)" -ForegroundColor Cyan
|
||||
}
|
||||
|
||||
# Check if login.py exists (should be on main after merge)
|
||||
if (Test-Path "login.py") {
|
||||
Write-Host "[PASS] File 'login.py' exists on main (from feature-login merge)" -ForegroundColor Green
|
||||
} else {
|
||||
Write-Host "[FAIL] File 'login.py' not found on main" -ForegroundColor Red
|
||||
Write-Host "[HINT] This file should appear after merging feature-login into main" -ForegroundColor Yellow
|
||||
$allChecksPassed = $false
|
||||
}
|
||||
|
||||
# Check if app.py contains login integration
|
||||
if (Test-Path "app.py") {
|
||||
$appContent = Get-Content "app.py" -Raw
|
||||
if ($appContent -match "login") {
|
||||
Write-Host "[PASS] app.py contains login integration" -ForegroundColor Green
|
||||
} else {
|
||||
Write-Host "[FAIL] app.py doesn't contain login integration" -ForegroundColor Red
|
||||
Write-Host "[HINT] After merging, app.py should import and use the login module" -ForegroundColor Yellow
|
||||
$allChecksPassed = $false
|
||||
}
|
||||
} else {
|
||||
Write-Host "[FAIL] app.py not found" -ForegroundColor Red
|
||||
$allChecksPassed = $false
|
||||
}
|
||||
|
||||
# Check for merge commit (indicates three-way merge happened)
|
||||
$mergeCommits = git log --merges --oneline 2>$null
|
||||
if ($mergeCommits) {
|
||||
Write-Host "[PASS] Merge commit found (three-way merge completed)" -ForegroundColor Green
|
||||
|
||||
# Get the merge commit message
|
||||
$mergeMessage = git log --merges --format=%s -1 2>$null
|
||||
Write-Host "[INFO] Merge commit message: '$mergeMessage'" -ForegroundColor Cyan
|
||||
} else {
|
||||
Write-Host "[FAIL] No merge commit found" -ForegroundColor Red
|
||||
Write-Host "[HINT] Merge feature-login into main with: git merge feature-login" -ForegroundColor Yellow
|
||||
$allChecksPassed = $false
|
||||
}
|
||||
|
||||
# Check if both branches contributed commits (true three-way merge)
|
||||
$totalCommits = git rev-list --count HEAD 2>$null
|
||||
if ($totalCommits -ge 4) {
|
||||
Write-Host "[PASS] Repository has $totalCommits commits (branches diverged properly)" -ForegroundColor Green
|
||||
} else {
|
||||
Write-Host "[WARN] Repository has only $totalCommits commits (expected at least 4)" -ForegroundColor Yellow
|
||||
Write-Host "[INFO] This might still be correct if you deleted commits" -ForegroundColor Cyan
|
||||
}
|
||||
|
||||
Set-Location ..
|
||||
|
||||
# Final summary
|
||||
if ($allChecksPassed) {
|
||||
Write-Host "`n" -NoNewline
|
||||
Write-Host "=====================================" -ForegroundColor Green
|
||||
Write-Host " CONGRATULATIONS! CHALLENGE PASSED!" -ForegroundColor Green
|
||||
Write-Host "=====================================" -ForegroundColor Green
|
||||
Write-Host "`nYou've successfully completed your first branch merge!" -ForegroundColor Cyan
|
||||
Write-Host "You now understand:" -ForegroundColor Cyan
|
||||
Write-Host " - How to merge branches with git merge" -ForegroundColor White
|
||||
Write-Host " - What a merge commit is and why it's created" -ForegroundColor White
|
||||
Write-Host " - How divergent branches are combined" -ForegroundColor White
|
||||
Write-Host " - How to visualize merges with git log --graph" -ForegroundColor White
|
||||
Write-Host "`nNext up: Module 05 - Merge Conflicts!" -ForegroundColor Yellow
|
||||
Write-Host "You'll learn what happens when Git can't automatically merge." -ForegroundColor Cyan
|
||||
Write-Host ""
|
||||
} else {
|
||||
Write-Host "`n[SUMMARY] Some checks failed. Review the hints above and try again." -ForegroundColor Red
|
||||
Write-Host "[INFO] You can run this verification script as many times as needed." -ForegroundColor Yellow
|
||||
Write-Host ""
|
||||
exit 1
|
||||
}
|
||||
@@ -1,481 +0,0 @@
|
||||
# Module 05: Merge Conflicts
|
||||
|
||||
## Learning Objectives
|
||||
|
||||
By the end of this module, you will:
|
||||
- Understand what merge conflicts are and why they occur
|
||||
- Identify merge conflicts in your repository
|
||||
- Read and interpret conflict markers
|
||||
- Resolve merge conflicts manually step-by-step
|
||||
- Complete a merge after resolving conflicts
|
||||
|
||||
## Challenge
|
||||
|
||||
### Setup
|
||||
|
||||
Run the setup script to create your challenge environment:
|
||||
|
||||
```powershell
|
||||
.\setup.ps1
|
||||
```
|
||||
|
||||
This will create a `challenge/` directory with a repository that has a merge conflict waiting to happen!
|
||||
|
||||
### Your Task
|
||||
|
||||
You have a repository with a main branch and a feature branch called `update-config`. Both branches have modified the same configuration file in different ways, creating a merge conflict.
|
||||
|
||||
**Your mission:**
|
||||
1. Attempt to merge the `update-config` branch into `main`
|
||||
2. Git will tell you there's a conflict - don't panic!
|
||||
3. Resolve the conflict by keeping BOTH settings (timeout AND debug)
|
||||
4. Complete the merge
|
||||
|
||||
**Steps to follow:**
|
||||
|
||||
1. Navigate to the challenge directory: `cd challenge`
|
||||
2. Make sure you're on main: `git branch`
|
||||
3. Try to merge: `git merge update-config`
|
||||
4. Git will report a conflict!
|
||||
5. Open `config.py` in your text editor
|
||||
6. Follow the step-by-step guide below to resolve it
|
||||
7. Save the file, stage it, and commit
|
||||
|
||||
## What Are Merge Conflicts?
|
||||
|
||||
A **merge conflict** occurs when Git cannot automatically combine changes because both branches modified the same lines in the same file.
|
||||
|
||||
**Example scenario:**
|
||||
```
|
||||
main branch changes line 5: TIMEOUT = 30
|
||||
feature branch changes line 5: DEBUG = True
|
||||
```
|
||||
|
||||
Git doesn't know which one you want! So it asks you to decide.
|
||||
|
||||
**When do conflicts happen?**
|
||||
- ✅ Two branches modify the same line(s)
|
||||
- ✅ One branch deletes a file that another branch modifies
|
||||
- ❌ Different files are changed (no conflict!)
|
||||
- ❌ Different lines in the same file are changed (no conflict!)
|
||||
|
||||
## Step-by-Step: Resolving Your First Conflict
|
||||
|
||||
### Step 1: Attempt the Merge
|
||||
|
||||
```bash
|
||||
cd challenge
|
||||
git merge update-config
|
||||
```
|
||||
|
||||
**You'll see:**
|
||||
```
|
||||
Auto-merging config.py
|
||||
CONFLICT (content): Merge conflict in config.py
|
||||
Automatic merge failed; fix conflicts and then commit the result.
|
||||
```
|
||||
|
||||
**Don't panic!** This is normal. Git is just asking for your help.
|
||||
|
||||
---
|
||||
|
||||
### Step 2: Check What Happened
|
||||
|
||||
```bash
|
||||
git status
|
||||
```
|
||||
|
||||
**You'll see:**
|
||||
```
|
||||
On branch main
|
||||
You have unmerged paths.
|
||||
(fix conflicts and run "git commit")
|
||||
(use "git merge --abort" to abort the merge)
|
||||
|
||||
Unmerged paths:
|
||||
(use "git add <file>..." to mark resolution)
|
||||
both modified: config.py
|
||||
```
|
||||
|
||||
This tells you that `config.py` needs your attention!
|
||||
|
||||
---
|
||||
|
||||
### Step 3: Open the Conflicted File
|
||||
|
||||
Open `config.py` in your text editor. You'll see something like this:
|
||||
|
||||
```python
|
||||
# config.py - Application configuration
|
||||
|
||||
APP_NAME = "My Application"
|
||||
VERSION = "1.0.0"
|
||||
<<<<<<< HEAD
|
||||
TIMEOUT = 30
|
||||
=======
|
||||
DEBUG = True
|
||||
>>>>>>> update-config
|
||||
```
|
||||
|
||||
**Let's break down what you're seeing:**
|
||||
|
||||
---
|
||||
|
||||
## Understanding Conflict Markers
|
||||
|
||||
Git has added special markers to show you both versions:
|
||||
|
||||
```python
|
||||
<<<<<<< HEAD ← Start of YOUR version (current branch)
|
||||
TIMEOUT = 30 ← What YOU have on main
|
||||
======= ← Divider between versions
|
||||
DEBUG = True ← What THEY have on update-config
|
||||
>>>>>>> update-config ← End of THEIR version
|
||||
```
|
||||
|
||||
**The three parts:**
|
||||
|
||||
1. **`<<<<<<< HEAD`** - Everything between here and `=======` is YOUR current branch's version
|
||||
2. **`=======`** - This separates the two versions
|
||||
3. **`>>>>>>> update-config`** - Everything between `=======` and here is the INCOMING branch's version
|
||||
|
||||
---
|
||||
|
||||
## Step 4: Decide What to Keep
|
||||
|
||||
You have three options:
|
||||
|
||||
**Option A: Keep YOUR version (main)**
|
||||
```python
|
||||
# config.py - Application configuration
|
||||
|
||||
APP_NAME = "My Application"
|
||||
VERSION = "1.0.0"
|
||||
TIMEOUT = 30
|
||||
```
|
||||
|
||||
**Option B: Keep THEIR version (update-config)**
|
||||
```python
|
||||
# config.py - Application configuration
|
||||
|
||||
APP_NAME = "My Application"
|
||||
VERSION = "1.0.0"
|
||||
DEBUG = True
|
||||
```
|
||||
|
||||
**Option C: Keep BOTH (this is what we want!)**
|
||||
```python
|
||||
# config.py - Application configuration
|
||||
|
||||
APP_NAME = "My Application"
|
||||
VERSION = "1.0.0"
|
||||
TIMEOUT = 30
|
||||
DEBUG = True
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Step 5: Edit the File to Resolve the Conflict
|
||||
|
||||
**What the file looks like NOW (with conflict markers):**
|
||||
```python
|
||||
# config.py - Application configuration
|
||||
|
||||
APP_NAME = "My Application"
|
||||
VERSION = "1.0.0"
|
||||
<<<<<<< HEAD
|
||||
TIMEOUT = 30
|
||||
=======
|
||||
DEBUG = True
|
||||
>>>>>>> update-config
|
||||
```
|
||||
|
||||
**What you need to do:**
|
||||
|
||||
1. **Delete** the line `<<<<<<< HEAD`
|
||||
2. **Keep** the line `TIMEOUT = 30`
|
||||
3. **Delete** the line `=======`
|
||||
4. **Keep** the line `DEBUG = True`
|
||||
5. **Delete** the line `>>>>>>> update-config`
|
||||
|
||||
**What the file should look like AFTER (conflict resolved):**
|
||||
```python
|
||||
# config.py - Application configuration
|
||||
|
||||
APP_NAME = "My Application"
|
||||
VERSION = "1.0.0"
|
||||
TIMEOUT = 30
|
||||
DEBUG = True
|
||||
```
|
||||
|
||||
**Save the file!**
|
||||
|
||||
---
|
||||
|
||||
## Step 6: Mark the Conflict as Resolved
|
||||
|
||||
Tell Git you've fixed the conflict by staging the file:
|
||||
|
||||
```bash
|
||||
git add config.py
|
||||
```
|
||||
|
||||
This tells Git: "I've resolved the conflict in this file, it's ready to be committed."
|
||||
|
||||
---
|
||||
|
||||
## Step 7: Check Your Status
|
||||
|
||||
```bash
|
||||
git status
|
||||
```
|
||||
|
||||
**You should see:**
|
||||
```
|
||||
On branch main
|
||||
All conflicts fixed but you are still merging.
|
||||
(use "git commit" to conclude merge)
|
||||
|
||||
Changes to be committed:
|
||||
modified: config.py
|
||||
```
|
||||
|
||||
Great! Git knows the conflict is resolved and is ready for you to complete the merge.
|
||||
|
||||
---
|
||||
|
||||
## Step 8: Complete the Merge
|
||||
|
||||
```bash
|
||||
git commit
|
||||
```
|
||||
|
||||
Git will open your editor with a default merge message:
|
||||
```
|
||||
Merge branch 'update-config'
|
||||
|
||||
# Conflicts:
|
||||
# config.py
|
||||
```
|
||||
|
||||
You can keep this message or customize it. Save and close the editor.
|
||||
|
||||
**Or use a one-liner:**
|
||||
```bash
|
||||
git commit -m "Merge update-config: resolve config conflict"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Step 9: Verify the Merge
|
||||
|
||||
```bash
|
||||
git log --oneline --graph
|
||||
```
|
||||
|
||||
You should see your merge commit!
|
||||
|
||||
```bash
|
||||
cat config.py
|
||||
```
|
||||
|
||||
Verify the file has BOTH settings and NO conflict markers!
|
||||
|
||||
---
|
||||
|
||||
## Visual Summary: Before and After
|
||||
|
||||
### Before Resolution (WRONG ❌)
|
||||
```python
|
||||
# config.py
|
||||
APP_NAME = "My Application"
|
||||
VERSION = "1.0.0"
|
||||
<<<<<<< HEAD
|
||||
TIMEOUT = 30
|
||||
=======
|
||||
DEBUG = True
|
||||
>>>>>>> update-config
|
||||
```
|
||||
**This will NOT run! Conflict markers are syntax errors!**
|
||||
|
||||
### After Resolution (CORRECT ✅)
|
||||
```python
|
||||
# config.py
|
||||
APP_NAME = "My Application"
|
||||
VERSION = "1.0.0"
|
||||
TIMEOUT = 30
|
||||
DEBUG = True
|
||||
```
|
||||
**Clean code, no markers, both settings preserved!**
|
||||
|
||||
---
|
||||
|
||||
## Common Mistakes to Avoid
|
||||
|
||||
### ❌ Mistake 1: Forgetting to Remove Conflict Markers
|
||||
```python
|
||||
TIMEOUT = 30
|
||||
======= ← Still has conflict marker!
|
||||
DEBUG = True
|
||||
```
|
||||
**This is invalid Python code and will crash!**
|
||||
|
||||
### ❌ Mistake 2: Committing Without Staging
|
||||
```bash
|
||||
# Wrong order:
|
||||
git commit # Error! File not staged
|
||||
|
||||
# Correct order:
|
||||
git add config.py # Stage first
|
||||
git commit # Then commit
|
||||
```
|
||||
|
||||
### ❌ Mistake 3: Only Keeping One Side When You Need Both
|
||||
```python
|
||||
# If the task asks for BOTH settings, this is wrong:
|
||||
TIMEOUT = 30
|
||||
# Missing DEBUG = True
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Useful Commands Reference
|
||||
|
||||
```bash
|
||||
# During a conflict
|
||||
git status # See which files have conflicts
|
||||
git diff # See the conflict in detail
|
||||
|
||||
# Resolving
|
||||
git add <file> # Mark file as resolved
|
||||
git commit # Complete the merge
|
||||
|
||||
# If you want to start over
|
||||
git merge --abort # Cancel the merge, go back to before
|
||||
|
||||
# After resolving
|
||||
git log --oneline --graph # See the merge commit
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Real-World Conflict Resolution Workflow
|
||||
|
||||
**Here's what you'll do every time you have a conflict:**
|
||||
|
||||
```bash
|
||||
# 1. Attempt the merge
|
||||
git merge feature-branch
|
||||
# → Git says: "CONFLICT! Fix it!"
|
||||
|
||||
# 2. Check what's wrong
|
||||
git status
|
||||
# → Shows which files have conflicts
|
||||
|
||||
# 3. Open the file, find the markers
|
||||
# <<<<<<< HEAD
|
||||
# your version
|
||||
# =======
|
||||
# their version
|
||||
# >>>>>>> branch
|
||||
|
||||
# 4. Edit the file to resolve
|
||||
# Remove the markers, keep what you need
|
||||
|
||||
# 5. Stage the resolved file
|
||||
git add config.py
|
||||
|
||||
# 6. Complete the merge
|
||||
git commit -m "Resolve conflict in config.py"
|
||||
|
||||
# 7. Celebrate! 🎉
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## What If You Get Stuck?
|
||||
|
||||
### Option 1: Abort the Merge
|
||||
```bash
|
||||
git merge --abort
|
||||
```
|
||||
This puts everything back to how it was before the merge. You can try again!
|
||||
|
||||
### Option 2: Ask for Help
|
||||
```bash
|
||||
git status # See what's going on
|
||||
git diff # See the conflict details
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Pro Tips
|
||||
|
||||
✅ **Use `git status` constantly** - It tells you exactly what to do next
|
||||
|
||||
✅ **Search for `<<<<<<<`** - Your editor's search function can find all conflicts quickly
|
||||
|
||||
✅ **Test after resolving** - Make sure your code still works!
|
||||
|
||||
✅ **Read both sides carefully** - Sometimes you need parts from each version
|
||||
|
||||
✅ **Take your time** - Conflicts are not a race. Understand what changed and why.
|
||||
|
||||
---
|
||||
|
||||
## Verification
|
||||
|
||||
Once you've resolved the conflict and completed the merge, verify your solution:
|
||||
|
||||
```powershell
|
||||
.\verify.ps1
|
||||
```
|
||||
|
||||
The verification will check that:
|
||||
- ✅ The merge conflict was resolved
|
||||
- ✅ The merge was completed successfully
|
||||
- ✅ BOTH settings are in the config file (TIMEOUT and DEBUG)
|
||||
- ✅ NO conflict markers remain
|
||||
- ✅ The merge commit exists in history
|
||||
|
||||
---
|
||||
|
||||
## Need to Start Over?
|
||||
|
||||
If you want to reset the challenge and start fresh:
|
||||
|
||||
```powershell
|
||||
.\reset.ps1
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## What You've Learned
|
||||
|
||||
🎉 **Congratulations!** You can now:
|
||||
- Recognize when a merge conflict occurs
|
||||
- Read and understand conflict markers
|
||||
- Edit files to resolve conflicts
|
||||
- Stage resolved files with `git add`
|
||||
- Complete a merge with `git commit`
|
||||
|
||||
**This is a critical skill!** Every developer encounters merge conflicts. Now you know exactly what to do when they happen.
|
||||
|
||||
---
|
||||
|
||||
## Quick Reference Card
|
||||
|
||||
**When you see a conflict:**
|
||||
|
||||
1. **Don't panic** ✅
|
||||
2. **Run `git status`** to see which files
|
||||
3. **Open the file** in your editor
|
||||
4. **Find the markers** (`<<<<<<<`, `=======`, `>>>>>>>`)
|
||||
5. **Decide what to keep** (one side, other side, or both)
|
||||
6. **Remove ALL markers** (the `<<<<<<<`, `=======`, `>>>>>>>` lines)
|
||||
7. **Save the file**
|
||||
8. **Stage it** (`git add filename`)
|
||||
9. **Commit** (`git commit`)
|
||||
10. **Verify** (`git status`, `git log --oneline --graph`)
|
||||
|
||||
**Remember:** The conflict markers are NOT valid code - they MUST be removed!
|
||||
@@ -1,22 +0,0 @@
|
||||
#!/usr/bin/env pwsh
|
||||
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Resets the merge conflicts challenge environment.
|
||||
|
||||
.DESCRIPTION
|
||||
Removes the existing challenge directory and runs setup.ps1
|
||||
to create a fresh challenge environment.
|
||||
#>
|
||||
|
||||
Write-Host "Resetting challenge environment..." -ForegroundColor Yellow
|
||||
|
||||
# Remove existing challenge directory if present
|
||||
if (Test-Path "challenge") {
|
||||
Remove-Item -Path "challenge" -Recurse -Force
|
||||
Write-Host "Removed existing challenge directory." -ForegroundColor Cyan
|
||||
}
|
||||
|
||||
# Run setup script
|
||||
Write-Host "Running setup script...`n" -ForegroundColor Cyan
|
||||
& ".\setup.ps1"
|
||||
@@ -1,97 +0,0 @@
|
||||
#!/usr/bin/env pwsh
|
||||
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Sets up the merge conflicts challenge environment.
|
||||
|
||||
.DESCRIPTION
|
||||
Creates a Git repository with a merge conflict scenario involving
|
||||
a configuration file that has been modified differently on two branches.
|
||||
#>
|
||||
|
||||
# Remove existing challenge directory if present
|
||||
if (Test-Path "challenge") {
|
||||
Write-Host "Removing existing challenge directory..." -ForegroundColor Yellow
|
||||
Remove-Item -Path "challenge" -Recurse -Force
|
||||
}
|
||||
|
||||
# Create challenge directory
|
||||
Write-Host "Creating challenge environment..." -ForegroundColor Cyan
|
||||
New-Item -ItemType Directory -Path "challenge" | Out-Null
|
||||
Set-Location "challenge"
|
||||
|
||||
# Initialize git repository
|
||||
git init | Out-Null
|
||||
git config user.name "Workshop User" | Out-Null
|
||||
git config user.email "user@workshop.local" | Out-Null
|
||||
|
||||
# Create initial config.json file
|
||||
$initialConfig = @"
|
||||
{
|
||||
"app": {
|
||||
"name": "MyApp",
|
||||
"version": "1.0.0",
|
||||
"port": 3000
|
||||
}
|
||||
}
|
||||
"@
|
||||
|
||||
Set-Content -Path "config.json" -Value $initialConfig
|
||||
git add config.json
|
||||
git commit -m "Initial configuration" | Out-Null
|
||||
|
||||
# Create feature branch
|
||||
git branch update-config | Out-Null
|
||||
|
||||
# On main branch: Add timeout setting
|
||||
$mainConfig = @"
|
||||
{
|
||||
"app": {
|
||||
"name": "MyApp",
|
||||
"version": "1.0.0",
|
||||
"port": 3000,
|
||||
"timeout": 5000
|
||||
}
|
||||
}
|
||||
"@
|
||||
|
||||
Set-Content -Path "config.json" -Value $mainConfig
|
||||
git add config.json
|
||||
git commit -m "Add timeout configuration" | Out-Null
|
||||
|
||||
# Switch to feature branch: Add debug setting (conflicting change)
|
||||
git switch update-config | Out-Null
|
||||
|
||||
$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 branch
|
||||
git switch main | Out-Null
|
||||
|
||||
# Return to module directory
|
||||
Set-Location ..
|
||||
|
||||
Write-Host "`n========================================" -ForegroundColor Green
|
||||
Write-Host "Challenge environment created!" -ForegroundColor Green
|
||||
Write-Host "========================================" -ForegroundColor Green
|
||||
Write-Host "`nYou are now on the 'main' branch." -ForegroundColor Cyan
|
||||
Write-Host "There is a branch called 'update-config' with conflicting changes." -ForegroundColor Cyan
|
||||
Write-Host "`nYour task:" -ForegroundColor Yellow
|
||||
Write-Host "1. Navigate to the challenge directory: cd challenge" -ForegroundColor White
|
||||
Write-Host "2. Try to merge the 'update-config' branch into 'main'" -ForegroundColor White
|
||||
Write-Host "3. Resolve the merge conflict in config.json" -ForegroundColor White
|
||||
Write-Host "4. Keep BOTH the timeout setting AND the debug setting" -ForegroundColor White
|
||||
Write-Host "5. Complete the merge" -ForegroundColor White
|
||||
Write-Host "`nRun '../verify.ps1' from the challenge directory to check your solution.`n" -ForegroundColor Cyan
|
||||
@@ -1,151 +0,0 @@
|
||||
#!/usr/bin/env pwsh
|
||||
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Verifies the merge conflicts challenge solution.
|
||||
|
||||
.DESCRIPTION
|
||||
Checks that the user successfully resolved the merge conflict,
|
||||
kept both configuration settings, and completed the merge.
|
||||
#>
|
||||
|
||||
Set-Location "challenge" -ErrorAction SilentlyContinue
|
||||
|
||||
# Check if challenge directory exists
|
||||
if (-not (Test-Path "../verify.ps1")) {
|
||||
Write-Host "Error: Please run this script from the module directory" -ForegroundColor Red
|
||||
exit 1
|
||||
}
|
||||
|
||||
if (-not (Test-Path ".")) {
|
||||
Write-Host "Error: Challenge directory not found. Run setup.ps1 first." -ForegroundColor Red
|
||||
Set-Location ..
|
||||
exit 1
|
||||
}
|
||||
|
||||
Write-Host "Verifying your solution..." -ForegroundColor Cyan
|
||||
|
||||
# Check if git repository exists
|
||||
if (-not (Test-Path ".git")) {
|
||||
Write-Host "[FAIL] No git repository found." -ForegroundColor Red
|
||||
Set-Location ..
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Check current branch
|
||||
$currentBranch = git branch --show-current 2>$null
|
||||
if ($currentBranch -ne "main") {
|
||||
Write-Host "[FAIL] You should be on the 'main' branch." -ForegroundColor Red
|
||||
Write-Host "Current branch: $currentBranch" -ForegroundColor Yellow
|
||||
Write-Host "Hint: Use 'git checkout main' to switch to main branch" -ForegroundColor Yellow
|
||||
Set-Location ..
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Check if there's an ongoing merge
|
||||
if (Test-Path ".git/MERGE_HEAD") {
|
||||
Write-Host "[FAIL] Merge is not complete. There are still unresolved conflicts." -ForegroundColor Red
|
||||
Write-Host "Hint: After resolving conflicts in config.json, use:" -ForegroundColor Yellow
|
||||
Write-Host " git add config.json" -ForegroundColor White
|
||||
Write-Host " git commit" -ForegroundColor White
|
||||
Set-Location ..
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Check if config.json exists
|
||||
if (-not (Test-Path "config.json")) {
|
||||
Write-Host "[FAIL] config.json file not found." -ForegroundColor Red
|
||||
Set-Location ..
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Read and parse config.json
|
||||
try {
|
||||
$configContent = Get-Content "config.json" -Raw
|
||||
$config = $configContent | ConvertFrom-Json
|
||||
} catch {
|
||||
Write-Host "[FAIL] config.json is not valid JSON." -ForegroundColor Red
|
||||
Write-Host "Make sure you removed all conflict markers (<<<<<<, ======, >>>>>>)" -ForegroundColor Yellow
|
||||
Set-Location ..
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Check for conflict markers (in case user forgot to remove them)
|
||||
if ($configContent -match "<<<<<<|======|>>>>>>") {
|
||||
Write-Host "[FAIL] Conflict markers still present in config.json" -ForegroundColor Red
|
||||
Write-Host "Remove all lines containing: <<<<<<, ======, >>>>>>" -ForegroundColor Yellow
|
||||
Set-Location ..
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Check if both timeout and debug settings exist
|
||||
$hasTimeout = $null -ne $config.app.timeout
|
||||
$hasDebug = $null -ne $config.app.debug
|
||||
|
||||
if (-not $hasTimeout) {
|
||||
Write-Host "[FAIL] The 'timeout' setting is missing from config.json" -ForegroundColor Red
|
||||
Write-Host "Hint: The resolved file should include both timeout AND debug settings" -ForegroundColor Yellow
|
||||
Set-Location ..
|
||||
exit 1
|
||||
}
|
||||
|
||||
if (-not $hasDebug) {
|
||||
Write-Host "[FAIL] The 'debug' setting is missing from config.json" -ForegroundColor Red
|
||||
Write-Host "Hint: The resolved file should include both timeout AND debug settings" -ForegroundColor Yellow
|
||||
Set-Location ..
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Validate the values
|
||||
if ($config.app.timeout -ne 5000) {
|
||||
Write-Host "[FAIL] The 'timeout' value should be 5000" -ForegroundColor Red
|
||||
Set-Location ..
|
||||
exit 1
|
||||
}
|
||||
|
||||
if ($config.app.debug -ne $true) {
|
||||
Write-Host "[FAIL] The 'debug' value should be true" -ForegroundColor Red
|
||||
Set-Location ..
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Check that merge commit exists
|
||||
$commitCount = (git rev-list --count HEAD 2>$null)
|
||||
if ($commitCount -lt 4) {
|
||||
Write-Host "[FAIL] Not enough commits. Expected at least 4 commits (including merge commit)." -ForegroundColor Red
|
||||
Write-Host "Current commits: $commitCount" -ForegroundColor Yellow
|
||||
Set-Location ..
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Check that the latest commit is a merge commit (has 2 parents)
|
||||
$parentCount = (git rev-list --parents -n 1 HEAD 2>$null).Split(' ').Count - 1
|
||||
if ($parentCount -ne 2) {
|
||||
Write-Host "[FAIL] The latest commit should be a merge commit." -ForegroundColor Red
|
||||
Write-Host "Hint: Complete the merge with 'git commit' after resolving conflicts" -ForegroundColor Yellow
|
||||
Set-Location ..
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Check that both branches are merged
|
||||
$branches = git branch --merged update-config 2>$null
|
||||
Write-Host "$branches $($branches -notmatch 'update-config')"
|
||||
if ($branches -notmatch "update-config") {
|
||||
Write-Host "[FAIL] The 'update-config' branch was not merged." -ForegroundColor Red
|
||||
Set-Location ..
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Success!
|
||||
Write-Host "`n========================================" -ForegroundColor Green
|
||||
Write-Host "SUCCESS! Challenge completed!" -ForegroundColor Green
|
||||
Write-Host "========================================" -ForegroundColor Green
|
||||
Write-Host "`nYou have successfully:" -ForegroundColor Cyan
|
||||
Write-Host "- Identified the merge conflict" -ForegroundColor White
|
||||
Write-Host "- Resolved the conflict by keeping both settings" -ForegroundColor White
|
||||
Write-Host "- Completed the merge with a proper merge commit" -ForegroundColor White
|
||||
Write-Host "`nYou now understand how to handle merge conflicts!" -ForegroundColor Green
|
||||
Write-Host "This is a critical skill for collaborative development.`n" -ForegroundColor Green
|
||||
|
||||
Set-Location ..
|
||||
exit 0
|
||||
86
README.md
86
README.md
@@ -8,19 +8,17 @@ Perfect for developers who want to move beyond basic Git usage and master profes
|
||||
|
||||
The workshop is organized into two tracks:
|
||||
|
||||
### 01 Essentials - Core Git Skills (9 modules)
|
||||
### 01 Essentials - Core Git Skills (7 modules)
|
||||
|
||||
Master fundamental Git concepts and collaborative workflows:
|
||||
|
||||
- **Module 01: Git Basics** - Initialize repositories, stage changes, make commits
|
||||
- **Module 02: Viewing History** - Use git log and git diff to explore project history
|
||||
- **Module 03: Branching Basics** - Create, switch, and manage branches
|
||||
- **Module 04: Merging** - Combine branches and understand merge workflows
|
||||
- **Module 05: Merge Conflicts** - Identify, understand, and resolve merge conflicts step-by-step
|
||||
- **Module 06: Cherry-Pick** - Apply specific commits from one branch to another
|
||||
- **Module 07: Reset vs Revert** - Understand when to rewrite history vs create new commits
|
||||
- **Module 08: Stash** - Temporarily save work without committing
|
||||
- **Module 09: Multiplayer Git** - **The Great Print Project** - Real cloud-based collaboration with teammates
|
||||
- **Module 03: Branching and Merging** - Create branches, merge them, and resolve conflicts (checkpoint-based)
|
||||
- **Module 04: Cherry-Pick** - Apply specific commits from one branch to another
|
||||
- **Module 05: Reset vs Revert** - Understand when to rewrite history vs create new commits
|
||||
- **Module 06: Stash** - Temporarily save work without committing
|
||||
- **Module 07: Multiplayer Git** - **The Great Print Project** - Real cloud-based collaboration with teammates
|
||||
|
||||
### 02 Advanced - Professional Techniques (6 modules)
|
||||
|
||||
@@ -46,7 +44,7 @@ Advanced Git workflows for power users:
|
||||
|
||||
**Quick Reference**: See [GIT-CHEATSHEET.md](GIT-CHEATSHEET.md) for a comprehensive list of all Git commands covered in this workshop. Don't worry about memorizing everything - use this as a reference when you need to look up command syntax!
|
||||
|
||||
### For Module 09: Multiplayer Git
|
||||
### For Module 07: Multiplayer Git
|
||||
|
||||
**This module is different!** It uses a real Git server for authentic collaboration:
|
||||
|
||||
@@ -138,9 +136,9 @@ You should see your name and email printed. This is required to make commits in
|
||||
$PSVersionTable.PSVersion
|
||||
```
|
||||
|
||||
### Python (for Module 09 only)
|
||||
### Python (for Module 07 only)
|
||||
|
||||
Module 09 (Multiplayer Git) uses Python:
|
||||
Module 07 (Multiplayer Git) uses Python:
|
||||
- **Python 3.6+** required to run the Great Print Project
|
||||
- Check: `python --version` or `python3 --version`
|
||||
|
||||
@@ -211,7 +209,7 @@ Follow the instructions in each module's README.md file.
|
||||
- **Progressive Difficulty**: Builds from basics to advanced Git techniques
|
||||
- **Reset Anytime**: Each local module includes a reset script for a fresh start
|
||||
- **Self-Paced**: Learn at your own speed with detailed README guides
|
||||
- **Real Collaboration**: Module 09 uses an actual Git server for authentic teamwork
|
||||
- **Real Collaboration**: Module 07 uses an actual Git server for authentic teamwork
|
||||
- **Comprehensive Coverage**: From `git init` to advanced rebasing and bisecting
|
||||
|
||||
## Learning Path
|
||||
@@ -220,19 +218,19 @@ The modules are designed to build on each other:
|
||||
|
||||
### Recommended Progression
|
||||
|
||||
**Phase 1: Core Fundamentals (Essentials 01-03)**
|
||||
- Git Basics, History, Branching
|
||||
- **Goal**: Understand commits and branches
|
||||
**Phase 1: Core Fundamentals (Essentials 01-02)**
|
||||
- Git Basics, History
|
||||
- **Goal**: Understand commits and history
|
||||
|
||||
**Phase 2: Collaboration Basics (Essentials 04-05)**
|
||||
- Merging, Merge Conflicts
|
||||
- **Goal**: Work with multiple branches
|
||||
**Phase 2: Collaboration Basics (Essentials 03)**
|
||||
- Branching and Merging (checkpoint-based: branching, merging, conflicts)
|
||||
- **Goal**: Work with multiple branches and resolve conflicts
|
||||
|
||||
**Phase 3: Workflow Tools (Essentials 06-08)**
|
||||
**Phase 3: Workflow Tools (Essentials 04-06)**
|
||||
- Cherry-Pick, Reset vs Revert, Stash
|
||||
- **Goal**: Manage your work effectively
|
||||
|
||||
**Phase 4: Real Collaboration (Essentials 09)**
|
||||
**Phase 4: Real Collaboration (Essentials 07)**
|
||||
- **Multiplayer Git - The Great Print Project**
|
||||
- **Goal**: Apply all skills with real teammates on a cloud server
|
||||
- **Note**: This is a capstone module - bring everything together!
|
||||
@@ -245,14 +243,14 @@ The modules are designed to build on each other:
|
||||
### Alternative Paths
|
||||
|
||||
**Fast Track (1 day workshop):**
|
||||
- Essentials 01-05 + Essentials 09 (Multiplayer)
|
||||
- Essentials 01-03 + Essentials 07 (Multiplayer)
|
||||
|
||||
**Solo Learner:**
|
||||
- Complete Essentials 01-08, skip 09 (requires partners and server)
|
||||
- Complete Essentials 01-06, skip 07 (requires partners and server)
|
||||
- Or complete all Advanced modules for deep mastery
|
||||
|
||||
**Team Workshop:**
|
||||
- Essentials 01-05 then jump to 09 (Multiplayer) for collaborative practice
|
||||
- Essentials 01-03 then jump to 07 (Multiplayer) for collaborative practice
|
||||
|
||||
## Tips for Success
|
||||
|
||||
@@ -263,7 +261,7 @@ The modules are designed to build on each other:
|
||||
- **Use `git log --oneline --graph --all`** frequently to visualize repository state
|
||||
- **If stuck**, check the Key Concepts section in the module's README
|
||||
- **Consider installing glow** for better markdown reading experience
|
||||
- **For Module 09**, work with a partner - collaboration is the point!
|
||||
- **For Module 07**, work with a partner - collaboration is the point!
|
||||
|
||||
## Skills You'll Master
|
||||
|
||||
@@ -307,7 +305,7 @@ Before distributing this workshop to attendees for self-study:
|
||||
2. Each module's `challenge/` directory will become its own independent git repository when attendees run `setup.ps1`
|
||||
3. This isolation ensures each module provides a clean learning environment
|
||||
|
||||
**Note**: Module 09 (Multiplayer) requires you to set up a Git server - see facilitator guide below.
|
||||
**Note**: Module 07 (Multiplayer) requires you to set up a Git server - see facilitator guide below.
|
||||
|
||||
### Facilitated Workshop
|
||||
|
||||
@@ -315,13 +313,13 @@ For running this as a full-day instructor-led workshop:
|
||||
|
||||
1. **See [WORKSHOP-AGENDA.md](WORKSHOP-AGENDA.md)** - Complete agenda with timing, activities, and facilitation tips
|
||||
2. **See [PRESENTATION-OUTLINE.md](PRESENTATION-OUTLINE.md)** - Slide deck outline for presentations
|
||||
3. **Workshop covers:** Essentials 01-05 + Module 09 (Multiplayer collaboration exercise)
|
||||
3. **Workshop covers:** Essentials 01-05 + Module 07 (Multiplayer collaboration exercise)
|
||||
4. **Duration:** 6-7 hours including breaks
|
||||
5. **Format:** Mix of presentation, live demos, and hands-on challenges
|
||||
|
||||
**Facilitator preparation:**
|
||||
- Review the workshop agenda thoroughly
|
||||
- Set up Git server for Module 09 (see below)
|
||||
- Set up Git server for Module 07 (see below)
|
||||
- Ensure all participants have prerequisites installed (Git, PowerShell, Python)
|
||||
- Prepare slides using the presentation outline
|
||||
- Test all modules on a clean machine
|
||||
@@ -329,9 +327,9 @@ For running this as a full-day instructor-led workshop:
|
||||
|
||||
The workshop format combines instructor-led sessions with self-paced hands-on modules for an engaging learning experience.
|
||||
|
||||
### Setting Up Module 09: Multiplayer Git
|
||||
### Setting Up Module 07: Multiplayer Git
|
||||
|
||||
Module 09 requires a Git server for authentic collaboration. You have two options:
|
||||
Module 07 requires a Git server for authentic collaboration. You have two options:
|
||||
|
||||
**Option 1: Self-Hosted Gitea Server (Recommended)**
|
||||
|
||||
@@ -376,18 +374,16 @@ git-workshop/
|
||||
├── GITEA-SETUP.md # Self-hosted Git server setup
|
||||
├── install-glow.ps1 # Install glow markdown renderer
|
||||
│
|
||||
├── 01_essentials/ # Core Git skills (9 modules)
|
||||
├── 01_essentials/ # Core Git skills (7 modules)
|
||||
│ ├── 01-basics/ # Initialize, commit, status
|
||||
│ ├── 02-history/ # Log, diff, show
|
||||
│ ├── 03-branching/ # Create and switch branches
|
||||
│ ├── 04-merging/ # Merge branches
|
||||
│ ├── 05-merge-conflicts/ # Resolve conflicts step-by-step
|
||||
│ ├── 06-cherry-pick/ # Apply specific commits
|
||||
│ ├── 07-reset-vs-revert/ # Undo changes safely
|
||||
│ ├── 08-stash/ # Save work-in-progress
|
||||
│ └── 09-multiplayer/ # Real collaboration (cloud-based)
|
||||
│ ├── README.md # Student guide (1,408 lines)
|
||||
│ └── FACILITATOR-SETUP.md # Server setup guide (904 lines)
|
||||
│ ├── 03-branching-and-merging/ # Branches, merging, conflicts (checkpoint-based)
|
||||
│ ├── 04-cherry-pick/ # Apply specific commits
|
||||
│ ├── 05-reset-vs-revert/ # Undo changes safely
|
||||
│ ├── 06-stash/ # Save work-in-progress
|
||||
│ └── 07-multiplayer/ # Real collaboration (cloud-based)
|
||||
│ ├── README.md # Student guide
|
||||
│ └── FACILITATOR-SETUP.md # Server setup guide
|
||||
│
|
||||
└── 02_advanced/ # Professional techniques (6 modules)
|
||||
├── 01-rebasing/ # Linear history with rebase
|
||||
@@ -400,9 +396,9 @@ git-workshop/
|
||||
|
||||
## What's Unique About This Workshop
|
||||
|
||||
### The Great Print Project (Module 09)
|
||||
### The Great Print Project (Module 07)
|
||||
|
||||
Unlike any other Git tutorial, Module 09 provides **real collaborative experience**:
|
||||
Unlike any other Git tutorial, Module 07 provides **real collaborative experience**:
|
||||
|
||||
- **Real Git server**: Not simulated - actual cloud repository at https://git.frod.dk/multiplayer
|
||||
- **Real teammates**: Work in pairs on shared branches
|
||||
@@ -429,7 +425,7 @@ This is how professional developers actually work - no simulation, no shortcuts.
|
||||
**Q: Do I need to complete all modules?**
|
||||
A: No! Essentials 01-05 covers what most developers use daily. Complete 06-09 and Advanced modules to deepen your skills.
|
||||
|
||||
**Q: Can I do Module 09 (Multiplayer) without a partner?**
|
||||
**Q: Can I do Module 07 (Multiplayer) without a partner?**
|
||||
A: Not recommended - collaboration is the point. If solo, skip to Advanced modules or wait until you can pair with someone.
|
||||
|
||||
**Q: How long does the workshop take?**
|
||||
@@ -445,16 +441,16 @@ A:
|
||||
2. Check [GIT-CHEATSHEET.md](GIT-CHEATSHEET.md)
|
||||
3. Run `./reset.ps1` to start fresh
|
||||
4. Use `git status` and `git log --graph` to understand current state
|
||||
5. For Module 09, ask your partner or facilitator
|
||||
5. For Module 07, ask your partner or facilitator
|
||||
|
||||
**Q: Can I use this for a team workshop at my company?**
|
||||
A: Absolutely! See the "For Workshop Facilitators" section above. The materials are designed for both self-study and instructor-led workshops.
|
||||
|
||||
**Q: Do I need internet access?**
|
||||
A: Modules 01-08 work completely offline. Module 09 requires internet to access the Git server.
|
||||
A: Modules 01-08 work completely offline. Module 07 requires internet to access the Git server.
|
||||
|
||||
**Q: What if I prefer GitHub/GitLab instead of Gitea?**
|
||||
A: The skills are identical across all Git platforms. Module 09 uses Gitea but everything you learn applies to GitHub, GitLab, Bitbucket, etc.
|
||||
A: The skills are identical across all Git platforms. Module 07 uses Gitea but everything you learn applies to GitHub, GitLab, Bitbucket, etc.
|
||||
|
||||
---
|
||||
|
||||
|
||||
Reference in New Issue
Block a user