refactor: rename 01_essentials to 01-essentials
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!
|
||||
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
|
||||
}
|
||||
Reference in New Issue
Block a user