Compare commits
8 Commits
0a9f7c1842
...
e7ce41cbbc
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e7ce41cbbc | ||
|
|
34f2607b22 | ||
|
|
bb01592b6c | ||
|
|
5492f17a5a | ||
|
|
5f78245734 | ||
|
|
8d63b2d22e | ||
|
|
039debe744 | ||
|
|
a0a9a5a0d9 |
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
|
||||
}
|
||||
638
01-essentials/05-revert/README.md
Normal file
638
01-essentials/05-revert/README.md
Normal file
@@ -0,0 +1,638 @@
|
||||
# Module 05: Git Revert - Safe Undoing
|
||||
|
||||
## About This Module
|
||||
|
||||
Welcome to Module 05, where you'll learn the **safe, team-friendly way to undo changes** in Git. Unlike destructive commands that erase history, `git revert` creates new commits that undo previous changes while preserving the complete project history.
|
||||
|
||||
**Why revert is important:**
|
||||
- ✅ Safe for shared/pushed commits
|
||||
- ✅ Preserves complete history and audit trail
|
||||
- ✅ Transparent to your team
|
||||
- ✅ Can be undone itself if needed
|
||||
- ✅ Works with any commit in history
|
||||
|
||||
**Key principle:** Revert doesn't erase mistakes—it documents how you fixed them.
|
||||
|
||||
## Learning Objectives
|
||||
|
||||
By completing this module, you will:
|
||||
|
||||
1. Revert regular commits safely while preserving surrounding changes
|
||||
2. Revert merge commits using the `-m` flag
|
||||
3. Understand merge commit parent numbering
|
||||
4. Handle the re-merge problem that occurs after reverting merges
|
||||
5. Revert multiple commits at once
|
||||
6. Know when to use revert vs. other undo strategies
|
||||
|
||||
## Prerequisites
|
||||
|
||||
Before starting this module, you should be comfortable with:
|
||||
- Creating commits (`git commit`)
|
||||
- Viewing commit history (`git log`)
|
||||
- Understanding branches and merging (Module 03)
|
||||
|
||||
## Setup
|
||||
|
||||
Run the setup script to create the challenge environment:
|
||||
|
||||
```powershell
|
||||
./setup.ps1
|
||||
```
|
||||
|
||||
This creates a `challenge/` directory with three branches demonstrating different revert scenarios:
|
||||
- `regular-revert` - Basic commit reversion
|
||||
- `merge-revert` - Merge commit reversion
|
||||
- `multi-revert` - Multiple commit reversion
|
||||
|
||||
## Challenge 1: Reverting a Regular Commit
|
||||
|
||||
### Scenario
|
||||
|
||||
You're working on a calculator application. A developer added a `divide` function that crashes when dividing by zero. The bug was discovered after subsequent commits were made, so you can't just delete it—you need to revert it while keeping the commits that came after.
|
||||
|
||||
### Your Task
|
||||
|
||||
1. Navigate to the challenge directory:
|
||||
```bash
|
||||
cd challenge
|
||||
```
|
||||
|
||||
2. You should be on the `regular-revert` branch. View the commit history:
|
||||
```bash
|
||||
git log --oneline
|
||||
```
|
||||
|
||||
3. Find the commit with the broken divide function (message: "Add broken divide function - needs to be reverted!")
|
||||
|
||||
4. Revert that specific commit:
|
||||
```bash
|
||||
git revert <commit-hash>
|
||||
```
|
||||
|
||||
5. Git will open your editor for the revert commit message. The default message is fine—save and close.
|
||||
|
||||
### What to Observe
|
||||
|
||||
After reverting, check:
|
||||
|
||||
```bash
|
||||
# View the new revert commit
|
||||
git log --oneline
|
||||
|
||||
# Check that divide function is gone
|
||||
cat calculator.py | grep "def divide" # Should return nothing
|
||||
|
||||
# Check that modulo function still exists (it came after the bad commit)
|
||||
cat calculator.py | grep "def modulo" # Should find it
|
||||
|
||||
# Check that multiply function still exists (it came before the bad commit)
|
||||
cat calculator.py | grep "def multiply" # Should find it
|
||||
```
|
||||
|
||||
**Key insight:** Revert creates a new commit that undoes the changes from the target commit, but leaves all other commits intact.
|
||||
|
||||
### Understanding the Timeline
|
||||
|
||||
```
|
||||
Before revert:
|
||||
main.py (initial) → multiply (good) → divide (BAD) → modulo (good)
|
||||
↑
|
||||
We want to undo THIS
|
||||
|
||||
After revert:
|
||||
main.py (initial) → multiply (good) → divide (BAD) → modulo (good) → revert divide (new commit)
|
||||
↑
|
||||
Removes divide, keeps modulo
|
||||
```
|
||||
|
||||
The revert commit adds a new point in history that undoes the divide changes.
|
||||
|
||||
## Challenge 2: Reverting a Merge Commit
|
||||
|
||||
### Scenario
|
||||
|
||||
Your team merged a `feature-auth` branch that added authentication functionality. After deployment, you discovered the authentication system has critical security issues. You need to revert the entire merge while the security team redesigns the feature.
|
||||
|
||||
**This is different from reverting a regular commit!** Merge commits have **two parents**, so you must tell Git which parent to keep.
|
||||
|
||||
### Understanding Merge Commit Parents
|
||||
|
||||
When you merge a feature branch into main:
|
||||
|
||||
```
|
||||
feature-auth (parent 2)
|
||||
↓
|
||||
C---D
|
||||
/ \
|
||||
A---B-----M ← Merge commit (has TWO parents)
|
||||
↑
|
||||
parent 1 (main)
|
||||
```
|
||||
|
||||
The merge commit `M` has:
|
||||
- **Parent 1**: The branch you merged INTO (main)
|
||||
- **Parent 2**: The branch you merged FROM (feature-auth)
|
||||
|
||||
When reverting a merge, you must specify which parent to keep using the `-m` flag:
|
||||
- `-m 1` means "keep parent 1" (main) - **Most common**
|
||||
- `-m 2` means "keep parent 2" (feature-auth) - Rare
|
||||
|
||||
**In practice:** You almost always use `-m 1` to keep the main branch and undo the feature branch changes.
|
||||
|
||||
### Your Task
|
||||
|
||||
1. Switch to the merge-revert branch:
|
||||
```bash
|
||||
git switch merge-revert
|
||||
```
|
||||
|
||||
2. View the commit history and find the merge commit:
|
||||
```bash
|
||||
git log --oneline --graph
|
||||
```
|
||||
|
||||
Look for: "Merge feature-auth branch"
|
||||
|
||||
3. Revert the merge commit using `-m 1`:
|
||||
```bash
|
||||
git revert -m 1 <merge-commit-hash>
|
||||
```
|
||||
|
||||
**Explanation:**
|
||||
- `-m 1` tells Git to keep parent 1 (main branch)
|
||||
- This undoes all changes from the feature-auth branch
|
||||
- Creates a new "revert merge" commit
|
||||
|
||||
4. Save the default commit message and check the result:
|
||||
```bash
|
||||
# Verify auth.py is gone
|
||||
ls auth.py # Should not exist
|
||||
|
||||
# Verify calculator.py no longer imports auth
|
||||
cat calculator.py | grep "from auth" # Should return nothing
|
||||
```
|
||||
|
||||
### What Happens Without -m?
|
||||
|
||||
If you try to revert a merge commit without the `-m` flag:
|
||||
|
||||
```bash
|
||||
git revert <merge-commit-hash>
|
||||
# Error: commit <hash> is a merge but no -m option was given
|
||||
```
|
||||
|
||||
Git doesn't know which parent you want to keep, so it refuses to proceed.
|
||||
|
||||
### The Re-Merge Problem
|
||||
|
||||
**Important gotcha:** After reverting a merge, you **cannot simply re-merge** the same branch!
|
||||
|
||||
Here's why:
|
||||
|
||||
```
|
||||
Initial merge:
|
||||
A---B---M (merged feature-auth)
|
||||
↑
|
||||
All changes from feature-auth are now in main
|
||||
|
||||
After revert:
|
||||
A---B---M---R (reverted merge)
|
||||
↑
|
||||
Changes removed, but Git remembers they were merged
|
||||
|
||||
Attempting to re-merge:
|
||||
A---B---M---R---M2 (try to merge feature-auth again)
|
||||
↑
|
||||
Git thinks: "I already merged these commits,
|
||||
nothing new to add!" (Empty merge)
|
||||
```
|
||||
|
||||
**Solutions if you need to re-merge:**
|
||||
|
||||
1. **Revert the revert** (recommended):
|
||||
```bash
|
||||
git revert <revert-commit-hash>
|
||||
```
|
||||
This brings back all the feature-auth changes.
|
||||
|
||||
2. **Cherry-pick new commits** from the feature branch:
|
||||
```bash
|
||||
git cherry-pick <new-commits>
|
||||
```
|
||||
|
||||
3. **Merge with --no-ff** and resolve conflicts manually (advanced).
|
||||
|
||||
### When to Revert Merges
|
||||
|
||||
Revert merge commits when:
|
||||
- ✅ Feature causes production issues
|
||||
- ✅ Need to temporarily remove a feature
|
||||
- ✅ Discovered critical bugs after merging
|
||||
- ✅ Security issues require immediate rollback
|
||||
|
||||
Don't revert merges when:
|
||||
- ❌ You just need to fix a small bug (fix it with a new commit instead)
|
||||
- ❌ You plan to re-merge the same branch soon (use reset if local, or revert-the-revert later)
|
||||
|
||||
## Challenge 3: Reverting Multiple Commits
|
||||
|
||||
### Scenario
|
||||
|
||||
Two separate commits added broken mathematical functions (`square_root` and `logarithm`). Both have critical bugs and need to be removed. You can revert multiple commits at once.
|
||||
|
||||
### Your Task
|
||||
|
||||
1. Switch to the multi-revert branch:
|
||||
```bash
|
||||
git switch multi-revert
|
||||
```
|
||||
|
||||
2. View the commit history:
|
||||
```bash
|
||||
git log --oneline
|
||||
```
|
||||
|
||||
Find the two commits:
|
||||
- "Add broken square_root - REVERT THIS!"
|
||||
- "Add broken logarithm - REVERT THIS TOO!"
|
||||
|
||||
3. Revert both commits in one command:
|
||||
```bash
|
||||
git revert <commit-hash-1> <commit-hash-2>
|
||||
```
|
||||
|
||||
**Important:** List commits from **oldest to newest** for cleanest history.
|
||||
|
||||
Alternatively, revert them one at a time:
|
||||
```bash
|
||||
git revert <commit-hash-1>
|
||||
git revert <commit-hash-2>
|
||||
```
|
||||
|
||||
4. Git will prompt for a commit message for each revert. Accept the defaults.
|
||||
|
||||
5. Verify the result:
|
||||
```bash
|
||||
# Check that both bad functions are gone
|
||||
cat calculator.py | grep "def square_root" # Should return nothing
|
||||
cat calculator.py | grep "def logarithm" # Should return nothing
|
||||
|
||||
# Check that good functions remain
|
||||
cat calculator.py | grep "def power" # Should find it
|
||||
cat calculator.py | grep "def absolute" # Should find it
|
||||
```
|
||||
|
||||
### Multi-Revert Strategies
|
||||
|
||||
**Reverting a range of commits:**
|
||||
|
||||
```bash
|
||||
# Revert commits from A to B (inclusive)
|
||||
git revert A^..B
|
||||
|
||||
# Example: Revert last 3 commits
|
||||
git revert HEAD~3..HEAD
|
||||
```
|
||||
|
||||
**Reverting without auto-commit:**
|
||||
|
||||
```bash
|
||||
# Stage revert changes without committing
|
||||
git revert --no-commit <commit-hash>
|
||||
|
||||
# Review changes
|
||||
git diff --staged
|
||||
|
||||
# Commit when ready
|
||||
git commit
|
||||
```
|
||||
|
||||
This is useful when reverting multiple commits and you want one combined revert commit.
|
||||
|
||||
## Verification
|
||||
|
||||
Verify your solutions by running the verification script:
|
||||
|
||||
```bash
|
||||
cd .. # Return to module directory
|
||||
./verify.ps1
|
||||
```
|
||||
|
||||
The script checks that:
|
||||
- ✅ Revert commits were created (not destructive deletion)
|
||||
- ✅ Bad code is removed
|
||||
- ✅ Good code before and after is preserved
|
||||
- ✅ Merge commits still exist in history
|
||||
- ✅ Proper use of `-m` flag for merge reverts
|
||||
|
||||
## Command Reference
|
||||
|
||||
### Basic Revert
|
||||
|
||||
```bash
|
||||
# Revert a specific commit
|
||||
git revert <commit-hash>
|
||||
|
||||
# Revert the most recent commit
|
||||
git revert HEAD
|
||||
|
||||
# Revert the second-to-last commit
|
||||
git revert HEAD~1
|
||||
```
|
||||
|
||||
### Merge Commit Revert
|
||||
|
||||
```bash
|
||||
# Revert a merge commit (keep parent 1)
|
||||
git revert -m 1 <merge-commit-hash>
|
||||
|
||||
# Revert a merge commit (keep parent 2) - rare
|
||||
git revert -m 2 <merge-commit-hash>
|
||||
```
|
||||
|
||||
### Multiple Commits
|
||||
|
||||
```bash
|
||||
# Revert multiple specific commits
|
||||
git revert <hash1> <hash2> <hash3>
|
||||
|
||||
# Revert a range of commits (oldest^..newest)
|
||||
git revert <oldest-hash>^..<newest-hash>
|
||||
|
||||
# Revert last 3 commits
|
||||
git revert HEAD~3..HEAD
|
||||
```
|
||||
|
||||
### Revert Options
|
||||
|
||||
```bash
|
||||
# Revert but don't commit automatically
|
||||
git revert --no-commit <commit-hash>
|
||||
|
||||
# Revert and edit the commit message
|
||||
git revert --edit <commit-hash>
|
||||
|
||||
# Revert without opening editor (use default message)
|
||||
git revert --no-edit <commit-hash>
|
||||
|
||||
# Abort a revert in progress (if conflicts)
|
||||
git revert --abort
|
||||
|
||||
# Continue revert after resolving conflicts
|
||||
git revert --continue
|
||||
```
|
||||
|
||||
## When to Use Git Revert
|
||||
|
||||
Use `git revert` when:
|
||||
|
||||
- ✅ **Commits are already pushed** - Safe for shared history
|
||||
- ✅ **Working in a team** - Transparent to everyone
|
||||
- ✅ **Need audit trail** - Shows what was undone and why
|
||||
- ✅ **Public repositories** - Can't rewrite public history
|
||||
- ✅ **Undoing old commits** - Can revert commits from weeks ago
|
||||
- ✅ **Production hotfixes** - Safe emergency rollback
|
||||
|
||||
**Golden Rule:** If others might have your commits, use revert.
|
||||
|
||||
## When NOT to Use Git Revert
|
||||
|
||||
Consider alternatives when:
|
||||
|
||||
- ❌ **Commits are still local** - Use `git reset` instead (Module 06)
|
||||
- ❌ **Just want to edit a commit** - Use `git commit --amend`
|
||||
- ❌ **Haven't pushed yet** - Reset is cleaner for local cleanup
|
||||
- ❌ **Need to combine commits** - Use interactive rebase
|
||||
- ❌ **Reverting creates complex conflicts** - Might need manual fix forward
|
||||
|
||||
## Revert vs. Reset vs. Rebase
|
||||
|
||||
| Command | History | Safety | Use Case |
|
||||
|---------|---------|--------|----------|
|
||||
| **revert** | Preserves | ✅ Safe | Undo pushed commits |
|
||||
| **reset** | Erases | ⚠️ Dangerous | Clean up local commits |
|
||||
| **rebase** | Rewrites | ⚠️ Dangerous | Polish commit history |
|
||||
|
||||
**This module teaches revert.** You'll learn reset in Module 06.
|
||||
|
||||
## Handling Revert Conflicts
|
||||
|
||||
Sometimes reverting causes conflicts if subsequent changes touched the same code:
|
||||
|
||||
```bash
|
||||
# Start revert
|
||||
git revert <commit-hash>
|
||||
|
||||
# If conflicts occur:
|
||||
# Conflict in calculator.py
|
||||
# CONFLICT (content): Merge conflict in calculator.py
|
||||
```
|
||||
|
||||
**To resolve:**
|
||||
|
||||
1. Open conflicted files and fix conflicts (look for `<<<<<<<` markers)
|
||||
2. Stage resolved files:
|
||||
```bash
|
||||
git add <resolved-files>
|
||||
```
|
||||
3. Continue the revert:
|
||||
```bash
|
||||
git revert --continue
|
||||
```
|
||||
|
||||
Or abort if you change your mind:
|
||||
```bash
|
||||
git revert --abort
|
||||
```
|
||||
|
||||
## Common Mistakes
|
||||
|
||||
### 1. Forgetting -m for Merge Commits
|
||||
|
||||
```bash
|
||||
# ❌ Wrong - will fail
|
||||
git revert <merge-commit>
|
||||
|
||||
# ✅ Correct
|
||||
git revert -m 1 <merge-commit>
|
||||
```
|
||||
|
||||
### 2. Trying to Re-Merge After Revert
|
||||
|
||||
```bash
|
||||
# After reverting a merge:
|
||||
git revert -m 1 <merge-commit>
|
||||
|
||||
# ❌ This won't work as expected
|
||||
git merge feature-branch # Empty merge!
|
||||
|
||||
# ✅ Do this instead
|
||||
git revert <the-revert-commit> # Revert the revert
|
||||
```
|
||||
|
||||
### 3. Using Reset on Pushed Commits
|
||||
|
||||
```bash
|
||||
# ❌ NEVER do this with pushed commits
|
||||
git reset --hard HEAD~3
|
||||
|
||||
# ✅ Do this instead
|
||||
git revert HEAD~3..HEAD
|
||||
```
|
||||
|
||||
### 4. Reverting Commits in Wrong Order
|
||||
|
||||
When reverting multiple related commits, revert from newest to oldest:
|
||||
|
||||
```bash
|
||||
# If you have: A → B → C (and C depends on B)
|
||||
|
||||
# ✅ Correct order
|
||||
git revert C
|
||||
git revert B
|
||||
|
||||
# ❌ Wrong order (may cause conflicts)
|
||||
git revert B # Conflict! C still references B
|
||||
git revert C
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Write clear revert messages:**
|
||||
```bash
|
||||
git revert <hash> -m "Revert authentication - security issue #1234"
|
||||
```
|
||||
|
||||
2. **Link to issue tracking:**
|
||||
```
|
||||
Revert "Add new payment system"
|
||||
|
||||
This reverts commit abc123.
|
||||
|
||||
Critical bug in payment processing.
|
||||
See bug tracker: ISSUE-1234
|
||||
```
|
||||
|
||||
3. **Test after reverting:**
|
||||
- Run your test suite
|
||||
- Verify the application still works
|
||||
- Check no unintended changes occurred
|
||||
|
||||
4. **Communicate with team:**
|
||||
- Announce reverts in team chat
|
||||
- Explain why the revert was necessary
|
||||
- Provide timeline for re-introducing the feature
|
||||
|
||||
5. **Keep reverts focused:**
|
||||
- Revert the minimum necessary
|
||||
- Don't bundle multiple unrelated reverts
|
||||
- One problem = one revert commit
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### "Commit is a merge but no -m option was given"
|
||||
|
||||
**Problem:** Trying to revert a merge commit without `-m`.
|
||||
|
||||
**Solution:**
|
||||
```bash
|
||||
git revert -m 1 <merge-commit-hash>
|
||||
```
|
||||
|
||||
### "Empty Revert / No Changes"
|
||||
|
||||
**Problem:** Revert doesn't seem to do anything.
|
||||
|
||||
**Possible causes:**
|
||||
- Commit was already reverted
|
||||
- Subsequent commits already undid the changes
|
||||
- Wrong commit hash
|
||||
|
||||
**Solution:**
|
||||
```bash
|
||||
# Check what the commit actually changed
|
||||
git show <commit-hash>
|
||||
|
||||
# Check if already reverted
|
||||
git log --grep="Revert"
|
||||
```
|
||||
|
||||
### "Conflicts During Revert"
|
||||
|
||||
**Problem:** Revert causes merge conflicts.
|
||||
|
||||
**Why:** Subsequent commits modified the same code.
|
||||
|
||||
**Solution:**
|
||||
1. Manually resolve conflicts in affected files
|
||||
2. `git add <resolved-files>`
|
||||
3. `git revert --continue`
|
||||
|
||||
Or consider fixing forward with a new commit instead of reverting.
|
||||
|
||||
### "Can't Re-Merge After Reverting Merge"
|
||||
|
||||
**Problem:** After reverting a merge, re-merging the branch brings no changes.
|
||||
|
||||
**Solution:** Revert the revert commit:
|
||||
```bash
|
||||
# Find the revert commit
|
||||
git log --oneline
|
||||
|
||||
# Revert the revert (brings changes back)
|
||||
git revert <revert-commit-hash>
|
||||
```
|
||||
|
||||
## Advanced: Revert Internals
|
||||
|
||||
Understanding what revert does under the hood:
|
||||
|
||||
```bash
|
||||
# Revert creates a new commit with inverse changes
|
||||
git revert <commit-hash>
|
||||
|
||||
# This is equivalent to:
|
||||
git diff <commit-hash>^..<commit-hash> > changes.patch
|
||||
patch -R < changes.patch # Apply in reverse
|
||||
git add .
|
||||
git commit -m "Revert '<original message>'"
|
||||
```
|
||||
|
||||
**Key insight:** Revert computes the diff of the target commit, inverts it, and applies it as a new commit.
|
||||
|
||||
## Going Further
|
||||
|
||||
Now that you understand revert, you're ready for:
|
||||
|
||||
- **Module 06: Git Reset** - Learn the dangerous but powerful local history rewriting
|
||||
- **Module 07: Git Stash** - Temporarily set aside uncommitted changes
|
||||
- **Module 08: Multiplayer Git** - Collaborate with advanced workflows
|
||||
|
||||
## Summary
|
||||
|
||||
You've learned:
|
||||
|
||||
- ✅ `git revert` creates new commits that undo previous changes
|
||||
- ✅ Revert is safe for shared/pushed commits
|
||||
- ✅ Merge commits require `-m 1` or `-m 2` flag
|
||||
- ✅ Parent 1 = branch merged into, Parent 2 = branch merged from
|
||||
- ✅ Can't simply re-merge after reverting a merge
|
||||
- ✅ Multiple commits can be reverted in one command
|
||||
- ✅ Revert preserves complete history for audit trails
|
||||
|
||||
**The Golden Rule of Revert:** Use revert for any commit that might be shared with others.
|
||||
|
||||
## Next Steps
|
||||
|
||||
1. Complete all three challenge scenarios
|
||||
2. Run `./verify.ps1` to check your solutions
|
||||
3. Experiment with reverting different commits
|
||||
4. Move on to Module 06: Git Reset (dangerous but powerful!)
|
||||
|
||||
---
|
||||
|
||||
**Need Help?**
|
||||
- Review the command reference above
|
||||
- Check the troubleshooting section
|
||||
- Re-run `./setup.ps1` to start fresh
|
||||
- Practice reverting in different orders to understand the behavior
|
||||
24
01-essentials/05-revert/reset.ps1
Normal file
24
01-essentials/05-revert/reset.ps1
Normal file
@@ -0,0 +1,24 @@
|
||||
#!/usr/bin/env pwsh
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Resets the Module 05 challenge environment to start fresh.
|
||||
|
||||
.DESCRIPTION
|
||||
This script removes the challenge directory and re-runs setup.ps1
|
||||
to create a fresh challenge environment.
|
||||
#>
|
||||
|
||||
Write-Host "`n=== Resetting Module 05: Git Revert Challenge ===" -ForegroundColor Cyan
|
||||
|
||||
# Check if challenge directory exists
|
||||
if (Test-Path "challenge") {
|
||||
Write-Host "Removing existing challenge directory..." -ForegroundColor Yellow
|
||||
Remove-Item -Recurse -Force "challenge"
|
||||
Write-Host "[OK] Challenge directory removed" -ForegroundColor Green
|
||||
} else {
|
||||
Write-Host "[INFO] No existing challenge directory found" -ForegroundColor Yellow
|
||||
}
|
||||
|
||||
# Run setup to create fresh environment
|
||||
Write-Host "`nRunning setup to create fresh challenge environment..." -ForegroundColor Cyan
|
||||
& "$PSScriptRoot/setup.ps1"
|
||||
373
01-essentials/05-revert/setup.ps1
Normal file
373
01-essentials/05-revert/setup.ps1
Normal file
@@ -0,0 +1,373 @@
|
||||
#!/usr/bin/env pwsh
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Sets up the Module 05 challenge environment for learning git revert.
|
||||
|
||||
.DESCRIPTION
|
||||
This script creates a challenge directory with three branches demonstrating
|
||||
different revert scenarios:
|
||||
- regular-revert: Basic revert of a single bad commit
|
||||
- merge-revert: Reverting a merge commit with -m flag
|
||||
- multi-revert: Reverting multiple commits at once
|
||||
#>
|
||||
|
||||
Write-Host "`n=== Setting up Module 05: Git Revert Challenge ===" -ForegroundColor Cyan
|
||||
|
||||
# Remove existing challenge directory if it exists
|
||||
if (Test-Path "challenge") {
|
||||
Write-Host "Removing existing challenge directory..." -ForegroundColor Yellow
|
||||
Remove-Item -Recurse -Force "challenge"
|
||||
}
|
||||
|
||||
# Create fresh challenge directory
|
||||
Write-Host "Creating challenge directory..." -ForegroundColor Green
|
||||
New-Item -ItemType Directory -Path "challenge" | Out-Null
|
||||
Set-Location "challenge"
|
||||
|
||||
# Initialize Git repository
|
||||
Write-Host "Initializing Git repository..." -ForegroundColor Green
|
||||
git init | Out-Null
|
||||
|
||||
# Configure git for this repository
|
||||
git config user.name "Workshop Student"
|
||||
git config user.email "student@example.com"
|
||||
|
||||
# ============================================================================
|
||||
# SCENARIO 1: Regular Revert (Basic)
|
||||
# ============================================================================
|
||||
Write-Host "`nScenario 1: Creating regular-revert branch..." -ForegroundColor Cyan
|
||||
|
||||
# Initial commit
|
||||
$calcContent = @"
|
||||
# calculator.py - Simple calculator
|
||||
|
||||
def add(a, b):
|
||||
"""Add two numbers."""
|
||||
return a + b
|
||||
|
||||
def subtract(a, b):
|
||||
"""Subtract b from a."""
|
||||
return a - b
|
||||
"@
|
||||
Set-Content -Path "calculator.py" -Value $calcContent
|
||||
git add .
|
||||
git commit -m "Initial calculator implementation" | Out-Null
|
||||
|
||||
# Create regular-revert branch
|
||||
git switch -c regular-revert | Out-Null
|
||||
|
||||
# Good commit: Add multiply
|
||||
$calcContent = @"
|
||||
# calculator.py - Simple calculator
|
||||
|
||||
def add(a, b):
|
||||
"""Add two numbers."""
|
||||
return a + b
|
||||
|
||||
def subtract(a, b):
|
||||
"""Subtract b from a."""
|
||||
return a - b
|
||||
|
||||
def multiply(a, b):
|
||||
"""Multiply two numbers."""
|
||||
return a * b
|
||||
"@
|
||||
Set-Content -Path "calculator.py" -Value $calcContent
|
||||
git add .
|
||||
git commit -m "Add multiply function" | Out-Null
|
||||
|
||||
# BAD commit: Add broken divide function
|
||||
$calcContent = @"
|
||||
# calculator.py - Simple calculator
|
||||
|
||||
def add(a, b):
|
||||
"""Add two numbers."""
|
||||
return a + b
|
||||
|
||||
def subtract(a, b):
|
||||
"""Subtract b from a."""
|
||||
return a - b
|
||||
|
||||
def multiply(a, b):
|
||||
"""Multiply two numbers."""
|
||||
return a * b
|
||||
|
||||
def divide(a, b):
|
||||
"""Divide a by b - BROKEN: doesn't handle division by zero!"""
|
||||
return a / b # This will crash if b is 0!
|
||||
"@
|
||||
Set-Content -Path "calculator.py" -Value $calcContent
|
||||
git add .
|
||||
git commit -m "Add broken divide function - needs to be reverted!" | Out-Null
|
||||
|
||||
# Good commit: Add modulo (after bad commit)
|
||||
$calcContent = @"
|
||||
# calculator.py - Simple calculator
|
||||
|
||||
def add(a, b):
|
||||
"""Add two numbers."""
|
||||
return a + b
|
||||
|
||||
def subtract(a, b):
|
||||
"""Subtract b from a."""
|
||||
return a - b
|
||||
|
||||
def multiply(a, b):
|
||||
"""Multiply two numbers."""
|
||||
return a * b
|
||||
|
||||
def divide(a, b):
|
||||
"""Divide a by b - BROKEN: doesn't handle division by zero!"""
|
||||
return a / b # This will crash if b is 0!
|
||||
|
||||
def modulo(a, b):
|
||||
"""Return remainder of a divided by b."""
|
||||
return a % b
|
||||
"@
|
||||
Set-Content -Path "calculator.py" -Value $calcContent
|
||||
git add .
|
||||
git commit -m "Add modulo function" | Out-Null
|
||||
|
||||
Write-Host "[CREATED] regular-revert branch with bad divide commit" -ForegroundColor Green
|
||||
|
||||
# ============================================================================
|
||||
# SCENARIO 2: Merge Revert (Merge Commit with -m flag)
|
||||
# ============================================================================
|
||||
Write-Host "`nScenario 2: Creating merge-revert scenario..." -ForegroundColor Cyan
|
||||
|
||||
# Switch back to main
|
||||
git switch main | Out-Null
|
||||
|
||||
# Create merge-revert branch
|
||||
git switch -c merge-revert | Out-Null
|
||||
|
||||
# Create a feature branch to merge
|
||||
git switch -c feature-auth | Out-Null
|
||||
|
||||
# Add auth functionality
|
||||
$authContent = @"
|
||||
# auth.py - Authentication module
|
||||
|
||||
def login(username, password):
|
||||
\"\"\"Login user.\"\"\"
|
||||
print(f"Logging in {username}...")
|
||||
return True
|
||||
|
||||
def logout(username):
|
||||
\"\"\"Logout user.\"\"\"
|
||||
print(f"Logging out {username}...")
|
||||
return True
|
||||
"@
|
||||
Set-Content -Path "auth.py" -Value $authContent
|
||||
git add .
|
||||
git commit -m "Add authentication module" | Out-Null
|
||||
|
||||
# Add password validation
|
||||
$authContent = @"
|
||||
# auth.py - Authentication module
|
||||
|
||||
def validate_password(password):
|
||||
\"\"\"Validate password strength.\"\"\"
|
||||
return len(password) >= 8
|
||||
|
||||
def login(username, password):
|
||||
\"\"\"Login user.\"\"\"
|
||||
if not validate_password(password):
|
||||
print("Password too weak!")
|
||||
return False
|
||||
print(f"Logging in {username}...")
|
||||
return True
|
||||
|
||||
def logout(username):
|
||||
\"\"\"Logout user.\"\"\"
|
||||
print(f"Logging out {username}...")
|
||||
return True
|
||||
"@
|
||||
Set-Content -Path "auth.py" -Value $authContent
|
||||
git add .
|
||||
git commit -m "Add password validation" | Out-Null
|
||||
|
||||
# Integrate auth into calculator (part of the feature branch)
|
||||
$calcContent = @"
|
||||
# calculator.py - Simple calculator
|
||||
from auth import login
|
||||
|
||||
def add(a, b):
|
||||
"""Add two numbers."""
|
||||
return a + b
|
||||
|
||||
def subtract(a, b):
|
||||
"""Subtract b from a."""
|
||||
return a - b
|
||||
|
||||
def secure_divide(a, b, username):
|
||||
"""Secure divide - requires authentication."""
|
||||
if login(username, "password123"):
|
||||
return a / b
|
||||
return None
|
||||
"@
|
||||
Set-Content -Path "calculator.py" -Value $calcContent
|
||||
git add .
|
||||
git commit -m "Integrate auth into calculator" | Out-Null
|
||||
|
||||
# Switch back to merge-revert and merge feature-auth
|
||||
git switch merge-revert | Out-Null
|
||||
git merge feature-auth --no-ff -m "Merge feature-auth branch" | Out-Null
|
||||
|
||||
Write-Host "[CREATED] merge-revert branch with merge commit to revert" -ForegroundColor Green
|
||||
|
||||
# ============================================================================
|
||||
# SCENARIO 3: Multi Revert (Multiple Bad Commits)
|
||||
# ============================================================================
|
||||
Write-Host "`nScenario 3: Creating multi-revert branch..." -ForegroundColor Cyan
|
||||
|
||||
# Switch back to main
|
||||
git switch main | Out-Null
|
||||
|
||||
# Create multi-revert branch
|
||||
git switch -c multi-revert | Out-Null
|
||||
|
||||
# Reset calculator to simple version
|
||||
$calcContent = @"
|
||||
# calculator.py - Simple calculator
|
||||
|
||||
def add(a, b):
|
||||
"""Add two numbers."""
|
||||
return a + b
|
||||
|
||||
def subtract(a, b):
|
||||
"""Subtract b from a."""
|
||||
return a - b
|
||||
"@
|
||||
Set-Content -Path "calculator.py" -Value $calcContent
|
||||
git add .
|
||||
git commit -m "Reset to basic calculator" | Out-Null
|
||||
|
||||
# Good commit: Add power function
|
||||
$calcContent = @"
|
||||
# calculator.py - Simple calculator
|
||||
|
||||
def add(a, b):
|
||||
"""Add two numbers."""
|
||||
return a + b
|
||||
|
||||
def subtract(a, b):
|
||||
"""Subtract b from a."""
|
||||
return a - b
|
||||
|
||||
def power(a, b):
|
||||
"""Raise a to the power of b."""
|
||||
return a ** b
|
||||
"@
|
||||
Set-Content -Path "calculator.py" -Value $calcContent
|
||||
git add .
|
||||
git commit -m "Add power function" | Out-Null
|
||||
|
||||
# BAD commit 1: Add broken square_root
|
||||
$calcContent = @"
|
||||
# calculator.py - Simple calculator
|
||||
|
||||
def add(a, b):
|
||||
"""Add two numbers."""
|
||||
return a + b
|
||||
|
||||
def subtract(a, b):
|
||||
"""Subtract b from a."""
|
||||
return a - b
|
||||
|
||||
def power(a, b):
|
||||
"""Raise a to the power of b."""
|
||||
return a ** b
|
||||
|
||||
def square_root(a):
|
||||
"""BROKEN: Returns wrong result for negative numbers!"""
|
||||
return a ** 0.5 # This returns NaN for negative numbers!
|
||||
"@
|
||||
Set-Content -Path "calculator.py" -Value $calcContent
|
||||
git add .
|
||||
git commit -m "Add broken square_root - REVERT THIS!" | Out-Null
|
||||
|
||||
# BAD commit 2: Add broken logarithm
|
||||
$calcContent = @"
|
||||
# calculator.py - Simple calculator
|
||||
|
||||
def add(a, b):
|
||||
"""Add two numbers."""
|
||||
return a + b
|
||||
|
||||
def subtract(a, b):
|
||||
"""Subtract b from a."""
|
||||
return a - b
|
||||
|
||||
def power(a, b):
|
||||
"""Raise a to the power of b."""
|
||||
return a ** b
|
||||
|
||||
def square_root(a):
|
||||
"""BROKEN: Returns wrong result for negative numbers!"""
|
||||
return a ** 0.5 # This returns NaN for negative numbers!
|
||||
|
||||
def logarithm(a):
|
||||
"""BROKEN: Doesn't handle zero or negative numbers!"""
|
||||
import math
|
||||
return math.log(a) # This crashes for a <= 0!
|
||||
"@
|
||||
Set-Content -Path "calculator.py" -Value $calcContent
|
||||
git add .
|
||||
git commit -m "Add broken logarithm - REVERT THIS TOO!" | Out-Null
|
||||
|
||||
# Good commit: Add absolute value (after bad commits)
|
||||
$calcContent = @"
|
||||
# calculator.py - Simple calculator
|
||||
|
||||
def add(a, b):
|
||||
"""Add two numbers."""
|
||||
return a + b
|
||||
|
||||
def subtract(a, b):
|
||||
"""Subtract b from a."""
|
||||
return a - b
|
||||
|
||||
def power(a, b):
|
||||
"""Raise a to the power of b."""
|
||||
return a ** b
|
||||
|
||||
def square_root(a):
|
||||
"""BROKEN: Returns wrong result for negative numbers!"""
|
||||
return a ** 0.5 # This returns NaN for negative numbers!
|
||||
|
||||
def logarithm(a):
|
||||
"""BROKEN: Doesn't handle zero or negative numbers!"""
|
||||
import math
|
||||
return math.log(a) # This crashes for a <= 0!
|
||||
|
||||
def absolute(a):
|
||||
"""Return absolute value of a."""
|
||||
return abs(a)
|
||||
"@
|
||||
Set-Content -Path "calculator.py" -Value $calcContent
|
||||
git add .
|
||||
git commit -m "Add absolute value function" | Out-Null
|
||||
|
||||
Write-Host "[CREATED] multi-revert branch with two bad commits to revert" -ForegroundColor Green
|
||||
|
||||
# ============================================================================
|
||||
# Return to regular-revert to start
|
||||
# ============================================================================
|
||||
git switch regular-revert | Out-Null
|
||||
|
||||
# Return to module directory
|
||||
Set-Location ..
|
||||
|
||||
Write-Host "`n=== Setup Complete! ===" -ForegroundColor Green
|
||||
Write-Host "`nThree revert scenarios have been created:" -ForegroundColor Cyan
|
||||
Write-Host " 1. regular-revert - Revert a single bad commit (basic)" -ForegroundColor White
|
||||
Write-Host " 2. merge-revert - Revert a merge commit with -m flag" -ForegroundColor White
|
||||
Write-Host " 3. multi-revert - Revert multiple bad commits" -ForegroundColor White
|
||||
Write-Host "`nYou are currently on the 'regular-revert' branch." -ForegroundColor Cyan
|
||||
Write-Host "`nNext steps:" -ForegroundColor Cyan
|
||||
Write-Host " 1. cd challenge" -ForegroundColor White
|
||||
Write-Host " 2. Read the README.md for detailed instructions" -ForegroundColor White
|
||||
Write-Host " 3. Complete each revert challenge" -ForegroundColor White
|
||||
Write-Host " 4. Run '..\verify.ps1' to check your solutions" -ForegroundColor White
|
||||
Write-Host ""
|
||||
226
01-essentials/05-revert/verify.ps1
Normal file
226
01-essentials/05-revert/verify.ps1
Normal file
@@ -0,0 +1,226 @@
|
||||
#!/usr/bin/env pwsh
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Verifies the Module 05 challenge solutions.
|
||||
|
||||
.DESCRIPTION
|
||||
Checks that all three revert scenarios have been completed correctly:
|
||||
- regular-revert: Single commit reverted
|
||||
- merge-revert: Merge commit reverted with -m flag
|
||||
- multi-revert: Multiple commits reverted
|
||||
#>
|
||||
|
||||
Write-Host "`n=== Verifying Module 05: Git Revert Solutions ===" -ForegroundColor Cyan
|
||||
|
||||
$allChecksPassed = $true
|
||||
$originalDir = Get-Location
|
||||
|
||||
# Check if challenge directory exists
|
||||
if (-not (Test-Path "challenge")) {
|
||||
Write-Host "[FAIL] Challenge directory not found. Run setup.ps1 first." -ForegroundColor Red
|
||||
exit 1
|
||||
}
|
||||
|
||||
Set-Location "challenge"
|
||||
|
||||
# Check if git repository exists
|
||||
if (-not (Test-Path ".git")) {
|
||||
Write-Host "[FAIL] Not a git repository. Run setup.ps1 first." -ForegroundColor Red
|
||||
Set-Location $originalDir
|
||||
exit 1
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# SCENARIO 1: Regular Revert Verification
|
||||
# ============================================================================
|
||||
Write-Host "`n=== Scenario 1: Regular Revert ===" -ForegroundColor Cyan
|
||||
|
||||
git switch regular-revert 2>&1 | Out-Null
|
||||
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
Write-Host "[FAIL] regular-revert branch not found" -ForegroundColor Red
|
||||
$allChecksPassed = $false
|
||||
} else {
|
||||
# Check that a revert commit exists
|
||||
$revertCommit = git log --oneline --grep="Revert" 2>$null
|
||||
if ($revertCommit) {
|
||||
Write-Host "[PASS] Revert commit found" -ForegroundColor Green
|
||||
} else {
|
||||
Write-Host "[FAIL] No revert commit found" -ForegroundColor Red
|
||||
Write-Host "[HINT] Use: git revert <commit-hash>" -ForegroundColor Yellow
|
||||
$allChecksPassed = $false
|
||||
}
|
||||
|
||||
# Check that calculator.py exists
|
||||
if (Test-Path "calculator.py") {
|
||||
$calcContent = Get-Content "calculator.py" -Raw
|
||||
|
||||
# Check that divide function is NOT in the code (was reverted)
|
||||
if ($calcContent -notmatch "def divide") {
|
||||
Write-Host "[PASS] Broken divide function successfully reverted" -ForegroundColor Green
|
||||
} else {
|
||||
Write-Host "[FAIL] divide function still exists (should be reverted)" -ForegroundColor Red
|
||||
Write-Host "[HINT] The bad commit should be reverted, removing the divide function" -ForegroundColor Yellow
|
||||
$allChecksPassed = $false
|
||||
}
|
||||
|
||||
# Check that modulo function still exists (should be preserved)
|
||||
if ($calcContent -match "def modulo") {
|
||||
Write-Host "[PASS] modulo function preserved (good commit after bad one)" -ForegroundColor Green
|
||||
} else {
|
||||
Write-Host "[FAIL] modulo function missing (should still exist)" -ForegroundColor Red
|
||||
Write-Host "[HINT] Only revert the bad commit, not the good ones after it" -ForegroundColor Yellow
|
||||
$allChecksPassed = $false
|
||||
}
|
||||
|
||||
# Check that multiply function exists (should be preserved)
|
||||
if ($calcContent -match "def multiply") {
|
||||
Write-Host "[PASS] multiply function preserved (good commit before bad one)" -ForegroundColor Green
|
||||
} else {
|
||||
Write-Host "[FAIL] multiply function missing" -ForegroundColor Red
|
||||
$allChecksPassed = $false
|
||||
}
|
||||
} else {
|
||||
Write-Host "[FAIL] calculator.py not found" -ForegroundColor Red
|
||||
$allChecksPassed = $false
|
||||
}
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# SCENARIO 2: Merge Revert Verification
|
||||
# ============================================================================
|
||||
Write-Host "`n=== Scenario 2: Merge Revert ===" -ForegroundColor Cyan
|
||||
|
||||
git switch merge-revert 2>&1 | Out-Null
|
||||
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
Write-Host "[FAIL] merge-revert branch not found" -ForegroundColor Red
|
||||
$allChecksPassed = $false
|
||||
} else {
|
||||
# Check that a revert commit for the merge exists
|
||||
$revertMerge = git log --oneline --grep="Revert.*Merge" 2>$null
|
||||
if ($revertMerge) {
|
||||
Write-Host "[PASS] Merge revert commit found" -ForegroundColor Green
|
||||
} else {
|
||||
Write-Host "[FAIL] No merge revert commit found" -ForegroundColor Red
|
||||
Write-Host "[HINT] Use: git revert -m 1 <merge-commit-hash>" -ForegroundColor Yellow
|
||||
$allChecksPassed = $false
|
||||
}
|
||||
|
||||
# Check that the original merge commit still exists (revert doesn't erase it)
|
||||
$mergeCommit = git log --merges --oneline --grep="Merge feature-auth" 2>$null
|
||||
if ($mergeCommit) {
|
||||
Write-Host "[PASS] Original merge commit still in history (not erased)" -ForegroundColor Green
|
||||
} else {
|
||||
Write-Host "[INFO] Original merge commit not found (this is OK if you used a different approach)" -ForegroundColor Yellow
|
||||
}
|
||||
|
||||
# Check that auth.py no longer exists or its effects are reverted
|
||||
if (-not (Test-Path "auth.py")) {
|
||||
Write-Host "[PASS] auth.py removed (merge reverted successfully)" -ForegroundColor Green
|
||||
} else {
|
||||
Write-Host "[INFO] auth.py still exists (check if merge was fully reverted)" -ForegroundColor Yellow
|
||||
}
|
||||
|
||||
# Check that calculator.py exists
|
||||
if (Test-Path "calculator.py") {
|
||||
$calcContent = Get-Content "calculator.py" -Raw
|
||||
|
||||
# After reverting the merge, calculator shouldn't import auth
|
||||
if ($calcContent -notmatch "from auth import") {
|
||||
Write-Host "[PASS] Auth integration reverted from calculator.py" -ForegroundColor Green
|
||||
} else {
|
||||
Write-Host "[FAIL] calculator.py still imports auth (merge not fully reverted)" -ForegroundColor Red
|
||||
Write-Host "[HINT] Reverting the merge should remove the auth integration" -ForegroundColor Yellow
|
||||
$allChecksPassed = $false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# SCENARIO 3: Multi Revert Verification
|
||||
# ============================================================================
|
||||
Write-Host "`n=== Scenario 3: Multi Revert ===" -ForegroundColor Cyan
|
||||
|
||||
git switch multi-revert 2>&1 | Out-Null
|
||||
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
Write-Host "[FAIL] multi-revert branch not found" -ForegroundColor Red
|
||||
$allChecksPassed = $false
|
||||
} else {
|
||||
# Count revert commits
|
||||
$revertCommits = git log --oneline --grep="Revert" 2>$null
|
||||
$revertCount = ($revertCommits | Measure-Object).Count
|
||||
|
||||
if ($revertCount -ge 2) {
|
||||
Write-Host "[PASS] Found $revertCount revert commits (expected at least 2)" -ForegroundColor Green
|
||||
} else {
|
||||
Write-Host "[FAIL] Found only $revertCount revert commit(s), need at least 2" -ForegroundColor Red
|
||||
Write-Host "[HINT] Revert both bad commits: git revert <commit1> <commit2>" -ForegroundColor Yellow
|
||||
$allChecksPassed = $false
|
||||
}
|
||||
|
||||
# Check calculator.py content
|
||||
if (Test-Path "calculator.py") {
|
||||
$calcContent = Get-Content "calculator.py" -Raw
|
||||
|
||||
# Check that square_root is NOT in code (reverted)
|
||||
if ($calcContent -notmatch "def square_root") {
|
||||
Write-Host "[PASS] Broken square_root function reverted" -ForegroundColor Green
|
||||
} else {
|
||||
Write-Host "[FAIL] square_root function still exists (should be reverted)" -ForegroundColor Red
|
||||
$allChecksPassed = $false
|
||||
}
|
||||
|
||||
# Check that logarithm is NOT in code (reverted)
|
||||
if ($calcContent -notmatch "def logarithm") {
|
||||
Write-Host "[PASS] Broken logarithm function reverted" -ForegroundColor Green
|
||||
} else {
|
||||
Write-Host "[FAIL] logarithm function still exists (should be reverted)" -ForegroundColor Red
|
||||
$allChecksPassed = $false
|
||||
}
|
||||
|
||||
# Check that power function still exists (good commit before bad ones)
|
||||
if ($calcContent -match "def power") {
|
||||
Write-Host "[PASS] power function preserved" -ForegroundColor Green
|
||||
} else {
|
||||
Write-Host "[FAIL] power function missing (should still exist)" -ForegroundColor Red
|
||||
$allChecksPassed = $false
|
||||
}
|
||||
|
||||
# Check that absolute function still exists (good commit after bad ones)
|
||||
if ($calcContent -match "def absolute") {
|
||||
Write-Host "[PASS] absolute function preserved" -ForegroundColor Green
|
||||
} else {
|
||||
Write-Host "[FAIL] absolute function missing (should still exist)" -ForegroundColor Red
|
||||
$allChecksPassed = $false
|
||||
}
|
||||
} else {
|
||||
Write-Host "[FAIL] calculator.py not found" -ForegroundColor Red
|
||||
$allChecksPassed = $false
|
||||
}
|
||||
}
|
||||
|
||||
Set-Location $originalDir
|
||||
|
||||
# Final summary
|
||||
Write-Host ""
|
||||
if ($allChecksPassed) {
|
||||
Write-Host "=========================================" -ForegroundColor Green
|
||||
Write-Host " CONGRATULATIONS! ALL SCENARIOS PASSED!" -ForegroundColor Green
|
||||
Write-Host "=========================================" -ForegroundColor Green
|
||||
Write-Host "`nYou've mastered git revert!" -ForegroundColor Cyan
|
||||
Write-Host "You now understand:" -ForegroundColor Cyan
|
||||
Write-Host " ✓ Reverting regular commits safely" -ForegroundColor White
|
||||
Write-Host " ✓ Reverting merge commits with -m flag" -ForegroundColor White
|
||||
Write-Host " ✓ Reverting multiple commits at once" -ForegroundColor White
|
||||
Write-Host " ✓ Preserving history while undoing changes" -ForegroundColor White
|
||||
Write-Host "`nReady for Module 06: Git Reset!" -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
|
||||
}
|
||||
717
01-essentials/06-reset/README.md
Normal file
717
01-essentials/06-reset/README.md
Normal file
@@ -0,0 +1,717 @@
|
||||
# Module 06: Git Reset - Dangerous History Rewriting
|
||||
|
||||
## ⚠️ CRITICAL SAFETY WARNING ⚠️
|
||||
|
||||
**Git reset is DESTRUCTIVE and DANGEROUS when misused!**
|
||||
|
||||
Before using `git reset`, always ask yourself:
|
||||
|
||||
```
|
||||
Have I pushed these commits to a remote repository?
|
||||
├─ YES → ❌ DO NOT USE RESET!
|
||||
│ Use git revert instead (Module 05)
|
||||
│ Rewriting pushed history breaks collaboration!
|
||||
│
|
||||
└─ NO → ✅ Proceed with reset (local cleanup only)
|
||||
Choose your mode carefully:
|
||||
--soft (safest), --mixed (moderate), --hard (DANGEROUS)
|
||||
```
|
||||
|
||||
**The Golden Rule:** NEVER reset commits that have been pushed/shared.
|
||||
|
||||
## About This Module
|
||||
|
||||
Welcome to Module 06, where you'll learn the powerful but dangerous `git reset` command. Unlike `git revert` (Module 05) which safely creates new commits, **reset erases commits from history**.
|
||||
|
||||
**Why reset exists:**
|
||||
- ✅ Clean up messy local commit history before pushing
|
||||
- ✅ Undo commits you haven't shared yet
|
||||
- ✅ Unstage files from the staging area
|
||||
- ✅ Recover from mistakes (with reflog)
|
||||
|
||||
**Why reset is dangerous:**
|
||||
- ⚠️ Erases commits permanently (without reflog)
|
||||
- ⚠️ Breaks repositories if used on pushed commits
|
||||
- ⚠️ Can lose work if used incorrectly
|
||||
- ⚠️ Confuses teammates if they have your commits
|
||||
|
||||
**Key principle:** Reset is for polishing LOCAL history before sharing.
|
||||
|
||||
## Learning Objectives
|
||||
|
||||
By completing this module, you will:
|
||||
|
||||
1. Understand the three reset modes: --soft, --mixed, --hard
|
||||
2. Reset commits while keeping changes staged (--soft)
|
||||
3. Reset commits and unstage changes (--mixed)
|
||||
4. Reset commits and discard everything (--hard)
|
||||
5. Know when reset is appropriate (local only!)
|
||||
6. Understand when to use revert instead
|
||||
7. Use reflog to recover from mistakes
|
||||
|
||||
## Prerequisites
|
||||
|
||||
Before starting this module, you should:
|
||||
- Be comfortable with commits and staging (`git add`, `git commit`)
|
||||
- Understand `git revert` from Module 05
|
||||
- **Know the difference between local and pushed commits!**
|
||||
|
||||
## Setup
|
||||
|
||||
Run the setup script to create the challenge environment:
|
||||
|
||||
```powershell
|
||||
./setup.ps1
|
||||
```
|
||||
|
||||
This creates a `challenge/` directory with three branches demonstrating different reset modes:
|
||||
- `soft-reset` - Reset with --soft (keep changes staged)
|
||||
- `mixed-reset` - Reset with --mixed (unstage changes)
|
||||
- `hard-reset` - Reset with --hard (discard everything)
|
||||
|
||||
**Remember:** These are all LOCAL commits that have NEVER been pushed!
|
||||
|
||||
## Understanding Reset Modes
|
||||
|
||||
Git reset has three modes that control what happens to your changes:
|
||||
|
||||
| Mode | Commits | Staging Area | Working Directory |
|
||||
|------|---------|--------------|-------------------|
|
||||
| **--soft** | ✂️ Removed | ✅ Kept (staged) | ✅ Kept |
|
||||
| **--mixed** (default) | ✂️ Removed | ✂️ Cleared | ✅ Kept (unstaged) |
|
||||
| **--hard** | ✂️ Removed | ✂️ Cleared | ✂️ **LOST!** |
|
||||
|
||||
**Visual explanation:**
|
||||
|
||||
```
|
||||
Before reset (3 commits):
|
||||
A → B → C → HEAD
|
||||
|
||||
After git reset --soft HEAD~1:
|
||||
A → B → HEAD
|
||||
↑
|
||||
C's changes are staged
|
||||
|
||||
After git reset --mixed HEAD~1 (or just git reset HEAD~1):
|
||||
A → B → HEAD
|
||||
↑
|
||||
C's changes are unstaged (in working directory)
|
||||
|
||||
After git reset --hard HEAD~1:
|
||||
A → B → HEAD
|
||||
↑
|
||||
C's changes are GONE (discarded completely!)
|
||||
```
|
||||
|
||||
## Challenge 1: Soft Reset (Safest)
|
||||
|
||||
### Scenario
|
||||
|
||||
You committed "feature C" but immediately realized the implementation is wrong. You want to undo the commit but keep the changes staged so you can edit and re-commit them properly.
|
||||
|
||||
**Use case:** Fixing the last commit's message or contents.
|
||||
|
||||
### Your Task
|
||||
|
||||
1. Navigate to the challenge directory:
|
||||
```bash
|
||||
cd challenge
|
||||
```
|
||||
|
||||
2. You should be on the `soft-reset` branch. View the commits:
|
||||
```bash
|
||||
git log --oneline
|
||||
```
|
||||
|
||||
You should see:
|
||||
- "Add feature C - needs better implementation!"
|
||||
- "Add feature B"
|
||||
- "Add feature A"
|
||||
- "Initial project setup"
|
||||
|
||||
3. View the current state:
|
||||
```bash
|
||||
git status
|
||||
# Should be clean
|
||||
```
|
||||
|
||||
4. Reset the last commit with --soft:
|
||||
```bash
|
||||
git reset --soft HEAD~1
|
||||
```
|
||||
|
||||
5. Check what happened:
|
||||
```bash
|
||||
# Commit is gone
|
||||
git log --oneline
|
||||
# Should only show 3 commits now (feature C commit removed)
|
||||
|
||||
# Changes are still staged
|
||||
git status
|
||||
# Should show "Changes to be committed"
|
||||
|
||||
# View the staged changes
|
||||
git diff --cached
|
||||
# Should show feature C code ready to be re-committed
|
||||
```
|
||||
|
||||
### What to Observe
|
||||
|
||||
After `--soft` reset:
|
||||
- ✅ Commit removed from history
|
||||
- ✅ Changes remain in staging area
|
||||
- ✅ Working directory unchanged
|
||||
- ✅ Ready to edit and re-commit
|
||||
|
||||
**When to use --soft:**
|
||||
- Fix the last commit message (though `commit --amend` is simpler)
|
||||
- Combine multiple commits into one
|
||||
- Re-do a commit with better changes
|
||||
|
||||
## Challenge 2: Mixed Reset (Default, Moderate)
|
||||
|
||||
### Scenario
|
||||
|
||||
You committed two experimental features that aren't ready. You want to remove both commits and have the changes back in your working directory (unstaged) so you can review and selectively re-commit them.
|
||||
|
||||
**Use case:** Undoing commits and starting over with more careful staging.
|
||||
|
||||
### Your Task
|
||||
|
||||
1. Switch to the mixed-reset branch:
|
||||
```bash
|
||||
git switch mixed-reset
|
||||
```
|
||||
|
||||
2. View the commits:
|
||||
```bash
|
||||
git log --oneline
|
||||
```
|
||||
|
||||
You should see:
|
||||
- "Add debug mode - REMOVE THIS TOO!"
|
||||
- "Add experimental feature X - REMOVE THIS!"
|
||||
- "Add logging system"
|
||||
- "Add application lifecycle"
|
||||
|
||||
3. Reset the last TWO commits (default is --mixed):
|
||||
```bash
|
||||
git reset HEAD~2
|
||||
# This is equivalent to: git reset --mixed HEAD~2
|
||||
```
|
||||
|
||||
4. Check what happened:
|
||||
```bash
|
||||
# Commits are gone
|
||||
git log --oneline
|
||||
# Should only show 2 commits (lifecycle + logging)
|
||||
|
||||
# NO staged changes
|
||||
git diff --cached
|
||||
# Should be empty
|
||||
|
||||
# Changes are in working directory (unstaged)
|
||||
git status
|
||||
# Should show "Changes not staged for commit"
|
||||
|
||||
# View the unstaged changes
|
||||
git diff
|
||||
# Should show experimental and debug code
|
||||
```
|
||||
|
||||
### What to Observe
|
||||
|
||||
After `--mixed` reset (the default):
|
||||
- ✅ Commits removed from history
|
||||
- ✅ Staging area cleared
|
||||
- ✅ Changes moved to working directory (unstaged)
|
||||
- ✅ Can selectively stage and re-commit parts
|
||||
|
||||
**When to use --mixed (default):**
|
||||
- Undo commits and start over with clean staging
|
||||
- Split one large commit into multiple smaller ones
|
||||
- Review changes before re-committing
|
||||
- Most common reset mode for cleanup
|
||||
|
||||
## Challenge 3: Hard Reset (MOST DANGEROUS!)
|
||||
|
||||
### ⚠️ EXTREME CAUTION REQUIRED ⚠️
|
||||
|
||||
**This will PERMANENTLY DELETE your work!**
|
||||
|
||||
Only use `--hard` when you're absolutely sure you want to throw away changes.
|
||||
|
||||
### Scenario
|
||||
|
||||
You committed completely broken code that you want to discard entirely. There's no salvaging it—you just want it gone.
|
||||
|
||||
**Use case:** Throwing away failed experiments or completely wrong code.
|
||||
|
||||
### Your Task
|
||||
|
||||
1. Switch to the hard-reset branch:
|
||||
```bash
|
||||
git switch hard-reset
|
||||
```
|
||||
|
||||
2. View the commits and the broken code:
|
||||
```bash
|
||||
git log --oneline
|
||||
# Shows "Add broken helper D - DISCARD COMPLETELY!"
|
||||
|
||||
cat utils.py
|
||||
# Shows the broken helper_d function
|
||||
```
|
||||
|
||||
3. Reset the last commit with --hard:
|
||||
```bash
|
||||
git reset --hard HEAD~1
|
||||
```
|
||||
|
||||
**WARNING:** This will permanently discard all changes from that commit!
|
||||
|
||||
4. Check what happened:
|
||||
```bash
|
||||
# Commit is gone
|
||||
git log --oneline
|
||||
# Should only show 2 commits
|
||||
|
||||
# NO staged changes
|
||||
git diff --cached
|
||||
# Empty
|
||||
|
||||
# NO unstaged changes
|
||||
git diff
|
||||
# Empty
|
||||
|
||||
# Working directory clean
|
||||
git status
|
||||
# "nothing to commit, working tree clean"
|
||||
|
||||
# File doesn't have broken code
|
||||
cat utils.py
|
||||
# helper_d is completely gone
|
||||
```
|
||||
|
||||
### What to Observe
|
||||
|
||||
After `--hard` reset:
|
||||
- ✅ Commit removed from history
|
||||
- ✅ Staging area cleared
|
||||
- ✅ Working directory reset to match
|
||||
- ⚠️ All changes from that commit PERMANENTLY DELETED
|
||||
|
||||
**When to use --hard:**
|
||||
- Discarding failed experiments completely
|
||||
- Throwing away work you don't want (CAREFUL!)
|
||||
- Cleaning up after mistakes (use reflog to recover if needed)
|
||||
- Resetting to a known good state
|
||||
|
||||
**⚠️ WARNING:** Files in the discarded commit are NOT gone forever—they're still in reflog for about 90 days. See "Recovery with Reflog" section below.
|
||||
|
||||
## Understanding HEAD~N Syntax
|
||||
|
||||
When resetting, you specify where to reset to:
|
||||
|
||||
```bash
|
||||
# Reset to the commit before HEAD
|
||||
git reset HEAD~1
|
||||
|
||||
# Reset to 2 commits before HEAD
|
||||
git reset HEAD~2
|
||||
|
||||
# Reset to 3 commits before HEAD
|
||||
git reset HEAD~3
|
||||
|
||||
# Reset to a specific commit hash
|
||||
git reset abc123
|
||||
|
||||
# Reset to a branch
|
||||
git reset main
|
||||
```
|
||||
|
||||
**Visualization:**
|
||||
|
||||
```
|
||||
HEAD~3 HEAD~2 HEAD~1 HEAD
|
||||
↓ ↓ ↓ ↓
|
||||
A → B → C → D → E
|
||||
↑
|
||||
Current commit
|
||||
```
|
||||
|
||||
- `git reset HEAD~1` moves HEAD from E to D
|
||||
- `git reset HEAD~2` moves HEAD from E to C
|
||||
- `git reset abc123` moves HEAD to that specific commit
|
||||
|
||||
## Verification
|
||||
|
||||
Verify your solutions by running the verification script:
|
||||
|
||||
```bash
|
||||
cd .. # Return to module directory
|
||||
./verify.ps1
|
||||
```
|
||||
|
||||
The script checks that:
|
||||
- ✅ Commits were reset (count decreased)
|
||||
- ✅ --soft: Changes remain staged
|
||||
- ✅ --mixed: Changes are unstaged
|
||||
- ✅ --hard: Everything is clean
|
||||
|
||||
## Recovery with Reflog
|
||||
|
||||
**Good news:** Even `--hard` reset doesn't immediately destroy commits!
|
||||
|
||||
Git keeps a "reflog" (reference log) of where HEAD has been for about 90 days. You can use this to recover "lost" commits.
|
||||
|
||||
### How to Recover from a Reset
|
||||
|
||||
1. View the reflog:
|
||||
```bash
|
||||
git reflog
|
||||
```
|
||||
|
||||
Output example:
|
||||
```
|
||||
abc123 HEAD@{0}: reset: moving to HEAD~1
|
||||
def456 HEAD@{1}: commit: Add broken helper D
|
||||
...
|
||||
```
|
||||
|
||||
2. Find the commit you want to recover (def456 in this example)
|
||||
|
||||
3. Reset back to it:
|
||||
```bash
|
||||
git reset def456
|
||||
# Or use the reflog reference:
|
||||
git reset HEAD@{1}
|
||||
```
|
||||
|
||||
4. Your "lost" commit is back!
|
||||
|
||||
### Reflog Safety Net
|
||||
|
||||
**Important:**
|
||||
- Reflog entries expire after ~90 days (configurable)
|
||||
- Reflog is LOCAL to your repository (not shared)
|
||||
- `git gc` can clean up old reflog entries
|
||||
- If you really lose a commit, check reflog first!
|
||||
|
||||
**Pro tip:** Before doing dangerous operations, note your current commit hash:
|
||||
```bash
|
||||
git log --oneline | head -1
|
||||
# abc123 Current work
|
||||
```
|
||||
|
||||
## When to Use Git Reset
|
||||
|
||||
Use `git reset` when:
|
||||
|
||||
- ✅ **Commits are LOCAL only** (never pushed)
|
||||
- ✅ **Cleaning up messy history** before sharing
|
||||
- ✅ **Undoing recent commits** you don't want
|
||||
- ✅ **Combining commits** into one clean commit
|
||||
- ✅ **Unstaging files** (mixed mode)
|
||||
- ✅ **Polishing commit history** before pull request
|
||||
|
||||
**Golden Rule:** Only reset commits that are local to your machine!
|
||||
|
||||
## When NOT to Use Git Reset
|
||||
|
||||
DO NOT use `git reset` when:
|
||||
|
||||
- ❌ **Commits are pushed/shared** with others
|
||||
- ❌ **Teammates have your commits** (breaks their repos)
|
||||
- ❌ **In public repositories** (use revert instead)
|
||||
- ❌ **Unsure if pushed** (check `git log origin/main`)
|
||||
- ❌ **On main/master branch** after push
|
||||
- ❌ **Need audit trail** of changes
|
||||
|
||||
**Use git revert instead** (Module 05) for pushed commits!
|
||||
|
||||
## Decision Tree: Reset vs Revert
|
||||
|
||||
```
|
||||
Need to undo a commit?
|
||||
│
|
||||
├─ Have you pushed this commit?
|
||||
│ │
|
||||
│ ├─ YES → Use git revert (Module 05)
|
||||
│ │ Safe for shared history
|
||||
│ │ Preserves complete audit trail
|
||||
│ │
|
||||
│ └─ NO → Can use git reset (local only)
|
||||
│ │
|
||||
│ ├─ Want to keep changes?
|
||||
│ │ │
|
||||
│ │ ├─ Keep staged → git reset --soft
|
||||
│ │ └─ Keep unstaged → git reset --mixed
|
||||
│ │
|
||||
│ └─ Discard everything? → git reset --hard
|
||||
│ (CAREFUL!)
|
||||
```
|
||||
|
||||
## Reset vs Revert vs Rebase
|
||||
|
||||
| Command | History | Safety | Use Case |
|
||||
|---------|---------|--------|----------|
|
||||
| **reset** | Erases | ⚠️ Dangerous | Local cleanup before push |
|
||||
| **revert** | Preserves | ✅ Safe | Undo pushed commits |
|
||||
| **rebase** | Rewrites | ⚠️ Dangerous | Polish history before push |
|
||||
|
||||
**This module teaches reset.** You learned revert in Module 05.
|
||||
|
||||
## Command Reference
|
||||
|
||||
### Basic Reset
|
||||
|
||||
```bash
|
||||
# Reset last commit, keep changes staged
|
||||
git reset --soft HEAD~1
|
||||
|
||||
# Reset last commit, unstage changes (default)
|
||||
git reset HEAD~1
|
||||
git reset --mixed HEAD~1 # Same as above
|
||||
|
||||
# Reset last commit, discard everything (DANGEROUS!)
|
||||
git reset --hard HEAD~1
|
||||
|
||||
# Reset multiple commits
|
||||
git reset --soft HEAD~3 # Last 3 commits
|
||||
|
||||
# Reset to specific commit
|
||||
git reset --soft abc123
|
||||
```
|
||||
|
||||
### Unstaging Files
|
||||
|
||||
```bash
|
||||
# Unstage a specific file (common use of reset)
|
||||
git reset HEAD filename.txt
|
||||
|
||||
# Unstage all files
|
||||
git reset HEAD .
|
||||
|
||||
# This is the same as:
|
||||
git restore --staged filename.txt # Modern syntax
|
||||
```
|
||||
|
||||
### Reflog and Recovery
|
||||
|
||||
```bash
|
||||
# View reflog
|
||||
git reflog
|
||||
|
||||
# Recover from reset
|
||||
git reset --hard HEAD@{1}
|
||||
git reset --hard abc123
|
||||
```
|
||||
|
||||
### Check Before Reset
|
||||
|
||||
```bash
|
||||
# Check if commits are pushed
|
||||
git log origin/main..HEAD
|
||||
# If output is empty, commits are pushed (DO NOT RESET)
|
||||
# If output shows commits, they're local (safe to reset)
|
||||
|
||||
# Another way to check
|
||||
git log --oneline --graph --all
|
||||
# Look for origin/main marker
|
||||
```
|
||||
|
||||
## Common Mistakes
|
||||
|
||||
### 1. Resetting Pushed Commits
|
||||
|
||||
```bash
|
||||
# ❌ NEVER do this if you've pushed!
|
||||
git push
|
||||
# ... time passes ...
|
||||
git reset --hard HEAD~3 # BREAKS teammate repos!
|
||||
|
||||
# ✅ Do this instead
|
||||
git revert HEAD~3..HEAD # Safe for shared history
|
||||
```
|
||||
|
||||
### 2. Using --hard Without Thinking
|
||||
|
||||
```bash
|
||||
# ❌ Dangerous - loses work!
|
||||
git reset --hard HEAD~1
|
||||
|
||||
# ✅ Better - keep changes to review
|
||||
git reset --mixed HEAD~1
|
||||
# Now you can review changes and decide
|
||||
```
|
||||
|
||||
### 3. Resetting Without Checking If Pushed
|
||||
|
||||
```bash
|
||||
# ❌ Risky - are these commits pushed?
|
||||
git reset HEAD~5
|
||||
|
||||
# ✅ Check first
|
||||
git log origin/main..HEAD # Local commits only
|
||||
git reset HEAD~5 # Now safe if output showed commits
|
||||
```
|
||||
|
||||
### 4. Forgetting Reflog Exists
|
||||
|
||||
```bash
|
||||
# ❌ Panic after accidental --hard reset
|
||||
# "I lost my work!"
|
||||
|
||||
# ✅ Check reflog first!
|
||||
git reflog # Find the "lost" commit
|
||||
git reset --hard HEAD@{1} # Recover it
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Always check if commits are pushed before reset:**
|
||||
```bash
|
||||
git log origin/main..HEAD
|
||||
```
|
||||
|
||||
2. **Prefer --mixed over --hard:**
|
||||
- You can always discard changes later
|
||||
- Hard to recover if you use --hard by mistake
|
||||
|
||||
3. **Commit often locally, reset before push:**
|
||||
- Make many small local commits
|
||||
- Reset/squash into clean commits before pushing
|
||||
|
||||
4. **Use descriptive commit messages even for local commits:**
|
||||
- Helps when reviewing before reset
|
||||
- Useful when checking reflog
|
||||
|
||||
5. **Know your escape hatch:**
|
||||
```bash
|
||||
git reflog # Your safety net!
|
||||
```
|
||||
|
||||
6. **Communicate with team:**
|
||||
- NEVER reset shared branches (main, develop, etc.)
|
||||
- Only reset your personal feature branches
|
||||
- Only before pushing!
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### "I accidentally reset with --hard and lost work!"
|
||||
|
||||
**Solution:** Check reflog:
|
||||
```bash
|
||||
git reflog
|
||||
# Find the commit before your reset
|
||||
git reset --hard HEAD@{1} # Or the commit hash
|
||||
```
|
||||
|
||||
**Prevention:** Always use --mixed first, then discard if really needed.
|
||||
|
||||
### "I reset but teammates still have my commits"
|
||||
|
||||
**Problem:** You reset and pushed with --force after they pulled.
|
||||
|
||||
**Impact:** Their repository is now broken/inconsistent.
|
||||
|
||||
**Solution:** Communicate! They need to:
|
||||
```bash
|
||||
git fetch
|
||||
git reset --hard origin/main # Or whatever branch
|
||||
```
|
||||
|
||||
**Prevention:** NEVER reset pushed commits!
|
||||
|
||||
### "Reset didn't do what I expected"
|
||||
|
||||
**Issue:** Wrong mode or wrong HEAD~N count.
|
||||
|
||||
**Solution:** Check current state:
|
||||
```bash
|
||||
git status
|
||||
git diff
|
||||
git diff --cached
|
||||
git log --oneline
|
||||
```
|
||||
|
||||
Undo the reset:
|
||||
```bash
|
||||
git reflog
|
||||
git reset HEAD@{1} # Go back to before your reset
|
||||
```
|
||||
|
||||
### "Can't reset - 'fatal: ambiguous argument HEAD~1'"
|
||||
|
||||
**Issue:** No commits to reset (probably first commit).
|
||||
|
||||
**Solution:** You can't reset before the first commit. If you want to remove the first commit entirely:
|
||||
```bash
|
||||
rm -rf .git # Nuclear option - deletes entire repo
|
||||
git init # Start over
|
||||
```
|
||||
|
||||
## Advanced: Reset Internals
|
||||
|
||||
Understanding what reset does under the hood:
|
||||
|
||||
```bash
|
||||
# Reset moves the branch pointer
|
||||
# Before:
|
||||
main → A → B → C (HEAD)
|
||||
|
||||
# After git reset --soft HEAD~1:
|
||||
main → A → B (HEAD)
|
||||
↑
|
||||
C still exists in reflog, just not in branch history
|
||||
|
||||
# The commit object C is still in .git/objects
|
||||
# It's just unreachable from any branch
|
||||
```
|
||||
|
||||
**Key insight:** Reset moves the HEAD and branch pointers backward. The commits still exist temporarily in reflog until garbage collection.
|
||||
|
||||
## Going Further
|
||||
|
||||
Now that you understand reset, you're ready for:
|
||||
|
||||
- **Module 07: Git Stash** - Temporarily save uncommitted work
|
||||
- **Module 08: Multiplayer Git** - Collaborate with complex workflows
|
||||
- **Interactive Rebase** - Advanced history polishing (beyond this workshop)
|
||||
|
||||
## Summary
|
||||
|
||||
You've learned:
|
||||
|
||||
- ✅ `git reset` rewrites history by moving HEAD backward
|
||||
- ✅ `--soft` keeps changes staged (safest)
|
||||
- ✅ `--mixed` (default) unstages changes
|
||||
- ✅ `--hard` discards everything (most dangerous)
|
||||
- ✅ NEVER reset pushed/shared commits
|
||||
- ✅ Use reflog to recover from mistakes
|
||||
- ✅ Check if commits are pushed before resetting
|
||||
- ✅ Use revert (Module 05) for shared commits
|
||||
|
||||
**The Critical Rule:** Reset is for LOCAL commits ONLY. Once you push, use revert!
|
||||
|
||||
## Next Steps
|
||||
|
||||
1. Complete all three challenge scenarios
|
||||
2. Run `./verify.ps1` to check your solutions
|
||||
3. Practice checking if commits are pushed before reset
|
||||
4. Move on to Module 07: Git Stash
|
||||
|
||||
---
|
||||
|
||||
**⚠️ FINAL REMINDER ⚠️**
|
||||
|
||||
**Before any `git reset` command, ask yourself:**
|
||||
|
||||
> "Have I pushed these commits?"
|
||||
|
||||
If YES → Use `git revert` instead!
|
||||
|
||||
If NO → Proceed carefully, choose the right mode.
|
||||
|
||||
**When in doubt, use --mixed instead of --hard!**
|
||||
24
01-essentials/06-reset/reset.ps1
Normal file
24
01-essentials/06-reset/reset.ps1
Normal file
@@ -0,0 +1,24 @@
|
||||
#!/usr/bin/env pwsh
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Resets the Module 06 challenge environment to start fresh.
|
||||
|
||||
.DESCRIPTION
|
||||
This script removes the challenge directory and re-runs setup.ps1
|
||||
to create a fresh challenge environment.
|
||||
#>
|
||||
|
||||
Write-Host "`n=== Resetting Module 06: Git Reset Challenge ===" -ForegroundColor Cyan
|
||||
|
||||
# Check if challenge directory exists
|
||||
if (Test-Path "challenge") {
|
||||
Write-Host "Removing existing challenge directory..." -ForegroundColor Yellow
|
||||
Remove-Item -Recurse -Force "challenge"
|
||||
Write-Host "[OK] Challenge directory removed" -ForegroundColor Green
|
||||
} else {
|
||||
Write-Host "[INFO] No existing challenge directory found" -ForegroundColor Yellow
|
||||
}
|
||||
|
||||
# Run setup to create fresh environment
|
||||
Write-Host "`nRunning setup to create fresh challenge environment..." -ForegroundColor Cyan
|
||||
& "$PSScriptRoot/setup.ps1"
|
||||
348
01-essentials/06-reset/setup.ps1
Normal file
348
01-essentials/06-reset/setup.ps1
Normal file
@@ -0,0 +1,348 @@
|
||||
#!/usr/bin/env pwsh
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Sets up the Module 06 challenge environment for learning git reset.
|
||||
|
||||
.DESCRIPTION
|
||||
This script creates a challenge directory with three branches demonstrating
|
||||
different reset scenarios:
|
||||
- soft-reset: Reset with --soft (keeps changes staged)
|
||||
- mixed-reset: Reset with --mixed (unstages changes)
|
||||
- hard-reset: Reset with --hard (discards everything) + reflog recovery
|
||||
#>
|
||||
|
||||
Write-Host "`n=== Setting up Module 06: Git Reset Challenge ===" -ForegroundColor Cyan
|
||||
Write-Host "⚠️ WARNING: Git reset is DANGEROUS - use with extreme caution! ⚠️" -ForegroundColor Red
|
||||
|
||||
# Remove existing challenge directory if it exists
|
||||
if (Test-Path "challenge") {
|
||||
Write-Host "Removing existing challenge directory..." -ForegroundColor Yellow
|
||||
Remove-Item -Recurse -Force "challenge"
|
||||
}
|
||||
|
||||
# Create fresh challenge directory
|
||||
Write-Host "Creating challenge directory..." -ForegroundColor Green
|
||||
New-Item -ItemType Directory -Path "challenge" | Out-Null
|
||||
Set-Location "challenge"
|
||||
|
||||
# Initialize Git repository
|
||||
Write-Host "Initializing Git repository..." -ForegroundColor Green
|
||||
git init | Out-Null
|
||||
|
||||
# Configure git for this repository
|
||||
git config user.name "Workshop Student"
|
||||
git config user.email "student@example.com"
|
||||
|
||||
# ============================================================================
|
||||
# Create initial commit (shared by all scenarios)
|
||||
# ============================================================================
|
||||
$readmeContent = @"
|
||||
# Git Reset Practice
|
||||
|
||||
This repository contains practice scenarios for learning git reset.
|
||||
"@
|
||||
Set-Content -Path "README.md" -Value $readmeContent
|
||||
git add .
|
||||
git commit -m "Initial commit" | Out-Null
|
||||
|
||||
# ============================================================================
|
||||
# SCENARIO 1: Soft Reset (--soft)
|
||||
# ============================================================================
|
||||
Write-Host "`nScenario 1: Creating soft-reset branch..." -ForegroundColor Cyan
|
||||
|
||||
# Create soft-reset branch from initial commit
|
||||
git switch -c soft-reset | Out-Null
|
||||
|
||||
# Build up scenario 1 commits
|
||||
$projectContent = @"
|
||||
# project.py - Main project file
|
||||
|
||||
def initialize():
|
||||
"""Initialize the project."""
|
||||
print("Project initialized")
|
||||
|
||||
def main():
|
||||
initialize()
|
||||
print("Running application...")
|
||||
"@
|
||||
Set-Content -Path "project.py" -Value $projectContent
|
||||
git add .
|
||||
git commit -m "Initial project setup" | Out-Null
|
||||
|
||||
# Good commit: Add feature A
|
||||
$projectContent = @"
|
||||
# project.py - Main project file
|
||||
|
||||
def initialize():
|
||||
"""Initialize the project."""
|
||||
print("Project initialized")
|
||||
|
||||
def feature_a():
|
||||
"""Feature A implementation."""
|
||||
print("Feature A is working")
|
||||
|
||||
def main():
|
||||
initialize()
|
||||
feature_a()
|
||||
print("Running application...")
|
||||
"@
|
||||
Set-Content -Path "project.py" -Value $projectContent
|
||||
git add .
|
||||
git commit -m "Add feature A" | Out-Null
|
||||
|
||||
# Good commit: Add feature B
|
||||
$projectContent = @"
|
||||
# project.py - Main project file
|
||||
|
||||
def initialize():
|
||||
"""Initialize the project."""
|
||||
print("Project initialized")
|
||||
|
||||
def feature_a():
|
||||
"""Feature A implementation."""
|
||||
print("Feature A is working")
|
||||
|
||||
def feature_b():
|
||||
"""Feature B implementation."""
|
||||
print("Feature B is working")
|
||||
|
||||
def main():
|
||||
initialize()
|
||||
feature_a()
|
||||
feature_b()
|
||||
print("Running application...")
|
||||
"@
|
||||
Set-Content -Path "project.py" -Value $projectContent
|
||||
git add .
|
||||
git commit -m "Add feature B" | Out-Null
|
||||
|
||||
# BAD commit: Add feature C (wrong implementation)
|
||||
$projectContent = @"
|
||||
# project.py - Main project file
|
||||
|
||||
def initialize():
|
||||
"""Initialize the project."""
|
||||
print("Project initialized")
|
||||
|
||||
def feature_a():
|
||||
"""Feature A implementation."""
|
||||
print("Feature A is working")
|
||||
|
||||
def feature_b():
|
||||
"""Feature B implementation."""
|
||||
print("Feature B is working")
|
||||
|
||||
def feature_c():
|
||||
"""Feature C implementation - WRONG!"""
|
||||
print("Feature C has bugs!") # This needs to be re-implemented
|
||||
|
||||
def main():
|
||||
initialize()
|
||||
feature_a()
|
||||
feature_b()
|
||||
feature_c()
|
||||
print("Running application...")
|
||||
"@
|
||||
Set-Content -Path "project.py" -Value $projectContent
|
||||
git add .
|
||||
git commit -m "Add feature C - needs better implementation!" | Out-Null
|
||||
|
||||
Write-Host "[CREATED] soft-reset branch with commit to reset --soft" -ForegroundColor Green
|
||||
|
||||
# ============================================================================
|
||||
# SCENARIO 2: Mixed Reset (--mixed, default)
|
||||
# ============================================================================
|
||||
Write-Host "`nScenario 2: Creating mixed-reset branch..." -ForegroundColor Cyan
|
||||
|
||||
# Switch back to initial commit and create mixed-reset branch
|
||||
git switch main | Out-Null
|
||||
git switch -c mixed-reset | Out-Null
|
||||
|
||||
# Build up scenario 2 commits
|
||||
$appContent = @"
|
||||
# app.py - Application entry point
|
||||
|
||||
def start():
|
||||
"""Start the application."""
|
||||
print("Application started")
|
||||
|
||||
def stop():
|
||||
"""Stop the application."""
|
||||
print("Application stopped")
|
||||
"@
|
||||
Set-Content -Path "app.py" -Value $appContent
|
||||
git add .
|
||||
git commit -m "Add application lifecycle" | Out-Null
|
||||
|
||||
# Good commit: Add logging
|
||||
$appContent = @"
|
||||
# app.py - Application entry point
|
||||
|
||||
def log(message):
|
||||
"""Log a message."""
|
||||
print(f"[LOG] {message}")
|
||||
|
||||
def start():
|
||||
"""Start the application."""
|
||||
log("Application started")
|
||||
|
||||
def stop():
|
||||
"""Stop the application."""
|
||||
log("Application stopped")
|
||||
"@
|
||||
Set-Content -Path "app.py" -Value $appContent
|
||||
git add .
|
||||
git commit -m "Add logging system" | Out-Null
|
||||
|
||||
# BAD commit 1: Add experimental feature X
|
||||
$appContent = @"
|
||||
# app.py - Application entry point
|
||||
|
||||
def log(message):
|
||||
"""Log a message."""
|
||||
print(f"[LOG] {message}")
|
||||
|
||||
def experimental_feature_x():
|
||||
"""Experimental feature - NOT READY!"""
|
||||
log("Feature X is experimental and buggy")
|
||||
|
||||
def start():
|
||||
"""Start the application."""
|
||||
log("Application started")
|
||||
experimental_feature_x()
|
||||
|
||||
def stop():
|
||||
"""Stop the application."""
|
||||
log("Application stopped")
|
||||
"@
|
||||
Set-Content -Path "app.py" -Value $appContent
|
||||
git add .
|
||||
git commit -m "Add experimental feature X - REMOVE THIS!" | Out-Null
|
||||
|
||||
# BAD commit 2: Add debug mode (also not ready)
|
||||
$appContent = @"
|
||||
# app.py - Application entry point
|
||||
|
||||
DEBUG_MODE = True # Should not be committed!
|
||||
|
||||
def log(message):
|
||||
"""Log a message."""
|
||||
print(f"[LOG] {message}")
|
||||
|
||||
def experimental_feature_x():
|
||||
"""Experimental feature - NOT READY!"""
|
||||
log("Feature X is experimental and buggy")
|
||||
|
||||
def start():
|
||||
"""Start the application."""
|
||||
if DEBUG_MODE:
|
||||
log("DEBUG MODE ACTIVE!")
|
||||
log("Application started")
|
||||
experimental_feature_x()
|
||||
|
||||
def stop():
|
||||
"""Stop the application."""
|
||||
log("Application stopped")
|
||||
"@
|
||||
Set-Content -Path "app.py" -Value $appContent
|
||||
git add .
|
||||
git commit -m "Add debug mode - REMOVE THIS TOO!" | Out-Null
|
||||
|
||||
Write-Host "[CREATED] mixed-reset branch with commits to reset --mixed" -ForegroundColor Green
|
||||
|
||||
# ============================================================================
|
||||
# SCENARIO 3: Hard Reset (--hard) + Reflog Recovery
|
||||
# ============================================================================
|
||||
Write-Host "`nScenario 3: Creating hard-reset branch..." -ForegroundColor Cyan
|
||||
|
||||
# Switch back to main and create hard-reset branch
|
||||
git switch main | Out-Null
|
||||
git switch -c hard-reset | Out-Null
|
||||
|
||||
# Reset to basic state
|
||||
$utilsContent = @"
|
||||
# utils.py - Utility functions
|
||||
|
||||
def helper_a():
|
||||
"""Helper function A."""
|
||||
return "Helper A"
|
||||
|
||||
def helper_b():
|
||||
"""Helper function B."""
|
||||
return "Helper B"
|
||||
"@
|
||||
Set-Content -Path "utils.py" -Value $utilsContent
|
||||
git add .
|
||||
git commit -m "Add utility helpers" | Out-Null
|
||||
|
||||
# Good commit: Add helper C
|
||||
$utilsContent = @"
|
||||
# utils.py - Utility functions
|
||||
|
||||
def helper_a():
|
||||
"""Helper function A."""
|
||||
return "Helper A"
|
||||
|
||||
def helper_b():
|
||||
"""Helper function B."""
|
||||
return "Helper B"
|
||||
|
||||
def helper_c():
|
||||
"""Helper function C."""
|
||||
return "Helper C"
|
||||
"@
|
||||
Set-Content -Path "utils.py" -Value $utilsContent
|
||||
git add .
|
||||
git commit -m "Add helper C" | Out-Null
|
||||
|
||||
# BAD commit: Add broken helper D (completely wrong)
|
||||
$utilsContent = @"
|
||||
# utils.py - Utility functions
|
||||
|
||||
def helper_a():
|
||||
"""Helper function A."""
|
||||
return "Helper A"
|
||||
|
||||
def helper_b():
|
||||
"""Helper function B."""
|
||||
return "Helper B"
|
||||
|
||||
def helper_c():
|
||||
"""Helper function C."""
|
||||
return "Helper C"
|
||||
|
||||
def helper_d():
|
||||
"""COMPLETELY BROKEN - throw away!"""
|
||||
# This is all wrong and needs to be discarded
|
||||
broken_code = "This doesn't even make sense"
|
||||
return broken_code.nonexistent_method() # Will crash!
|
||||
"@
|
||||
Set-Content -Path "utils.py" -Value $utilsContent
|
||||
git add .
|
||||
git commit -m "Add broken helper D - DISCARD COMPLETELY!" | Out-Null
|
||||
|
||||
Write-Host "[CREATED] hard-reset branch with commit to reset --hard" -ForegroundColor Green
|
||||
|
||||
# ============================================================================
|
||||
# Return to soft-reset to start
|
||||
# ============================================================================
|
||||
git switch soft-reset | Out-Null
|
||||
|
||||
# Return to module directory
|
||||
Set-Location ..
|
||||
|
||||
Write-Host "`n=== Setup Complete! ===`n" -ForegroundColor Green
|
||||
Write-Host "Three reset scenarios have been created:" -ForegroundColor Cyan
|
||||
Write-Host " 1. soft-reset - Reset --soft (keep changes staged)" -ForegroundColor White
|
||||
Write-Host " 2. mixed-reset - Reset --mixed (unstage changes)" -ForegroundColor White
|
||||
Write-Host " 3. hard-reset - Reset --hard (discard everything) + reflog recovery" -ForegroundColor White
|
||||
Write-Host "`n⚠️ CRITICAL SAFETY REMINDER ⚠️" -ForegroundColor Red
|
||||
Write-Host "NEVER use git reset on commits that have been PUSHED!" -ForegroundColor Red
|
||||
Write-Host "These scenarios are LOCAL ONLY for practice." -ForegroundColor Yellow
|
||||
Write-Host "`nYou are currently on the 'soft-reset' branch." -ForegroundColor Cyan
|
||||
Write-Host "`nNext steps:" -ForegroundColor Cyan
|
||||
Write-Host " 1. cd challenge" -ForegroundColor White
|
||||
Write-Host " 2. Read the README.md for detailed instructions" -ForegroundColor White
|
||||
Write-Host " 3. Complete each reset challenge" -ForegroundColor White
|
||||
Write-Host " 4. Run '..\\verify.ps1' to check your solutions" -ForegroundColor White
|
||||
Write-Host ""
|
||||
231
01-essentials/06-reset/verify.ps1
Normal file
231
01-essentials/06-reset/verify.ps1
Normal file
@@ -0,0 +1,231 @@
|
||||
#!/usr/bin/env pwsh
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Verifies the Module 06 challenge solutions.
|
||||
|
||||
.DESCRIPTION
|
||||
Checks that all three reset scenarios have been completed correctly:
|
||||
- soft-reset: Commit reset but changes remain staged
|
||||
- mixed-reset: Commits reset and changes unstaged
|
||||
- hard-reset: Everything reset and discarded
|
||||
#>
|
||||
|
||||
Write-Host "`n=== Verifying Module 06: Git Reset Solutions ===" -ForegroundColor Cyan
|
||||
Write-Host "⚠️ Remember: NEVER reset pushed commits! ⚠️" -ForegroundColor Red
|
||||
|
||||
$allChecksPassed = $true
|
||||
$originalDir = Get-Location
|
||||
|
||||
# Check if challenge directory exists
|
||||
if (-not (Test-Path "challenge")) {
|
||||
Write-Host "[FAIL] Challenge directory not found. Run setup.ps1 first." -ForegroundColor Red
|
||||
exit 1
|
||||
}
|
||||
|
||||
Set-Location "challenge"
|
||||
|
||||
# Check if git repository exists
|
||||
if (-not (Test-Path ".git")) {
|
||||
Write-Host "[FAIL] Not a git repository. Run setup.ps1 first." -ForegroundColor Red
|
||||
Set-Location $originalDir
|
||||
exit 1
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# SCENARIO 1: Soft Reset Verification
|
||||
# ============================================================================
|
||||
Write-Host "`n=== Scenario 1: Soft Reset ===`n" -ForegroundColor Cyan
|
||||
|
||||
git switch soft-reset 2>&1 | Out-Null
|
||||
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
Write-Host "[FAIL] soft-reset branch not found" -ForegroundColor Red
|
||||
$allChecksPassed = $false
|
||||
} else {
|
||||
# Count commits (should be 4: Initial + project setup + feature A + feature B)
|
||||
$commitCount = [int](git rev-list --count HEAD 2>$null)
|
||||
|
||||
if ($commitCount -eq 4) {
|
||||
Write-Host "[PASS] Commit count is 4 (feature C commit was reset)" -ForegroundColor Green
|
||||
} else {
|
||||
Write-Host "[FAIL] Expected 4 commits, found $commitCount" -ForegroundColor Red
|
||||
Write-Host "[HINT] Use: git reset --soft HEAD~1" -ForegroundColor Yellow
|
||||
$allChecksPassed = $false
|
||||
}
|
||||
|
||||
# Check if changes are staged
|
||||
$stagedChanges = git diff --cached --name-only 2>$null
|
||||
if ($stagedChanges) {
|
||||
Write-Host "[PASS] Changes are staged (feature C code in staging area)" -ForegroundColor Green
|
||||
|
||||
# Verify the staged changes contain feature C code
|
||||
$stagedContent = git diff --cached 2>$null
|
||||
if ($stagedContent -match "feature_c") {
|
||||
Write-Host "[PASS] Staged changes contain feature C code" -ForegroundColor Green
|
||||
} else {
|
||||
Write-Host "[INFO] Staged changes don't seem to contain feature C" -ForegroundColor Yellow
|
||||
}
|
||||
} else {
|
||||
Write-Host "[FAIL] No staged changes found" -ForegroundColor Red
|
||||
Write-Host "[HINT] After --soft reset, changes should remain staged" -ForegroundColor Yellow
|
||||
$allChecksPassed = $false
|
||||
}
|
||||
|
||||
# Check working directory has no unstaged changes to tracked files
|
||||
$unstagedChanges = git diff --name-only 2>$null
|
||||
if (-not $unstagedChanges) {
|
||||
Write-Host "[PASS] No unstaged changes (all changes are staged)" -ForegroundColor Green
|
||||
} else {
|
||||
Write-Host "[INFO] Found unstaged changes (expected only staged changes)" -ForegroundColor Yellow
|
||||
}
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# SCENARIO 2: Mixed Reset Verification
|
||||
# ============================================================================
|
||||
Write-Host "`n=== Scenario 2: Mixed Reset ===`n" -ForegroundColor Cyan
|
||||
|
||||
git switch mixed-reset 2>&1 | Out-Null
|
||||
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
Write-Host "[FAIL] mixed-reset branch not found" -ForegroundColor Red
|
||||
$allChecksPassed = $false
|
||||
} else {
|
||||
# Count commits (should be 3: Initial + lifecycle + logging, bad commits removed)
|
||||
$commitCount = [int](git rev-list --count HEAD 2>$null)
|
||||
|
||||
if ($commitCount -eq 3) {
|
||||
Write-Host "[PASS] Commit count is 3 (both bad commits were reset)" -ForegroundColor Green
|
||||
} elseif ($commitCount -eq 4) {
|
||||
Write-Host "[INFO] Commit count is 4 (one commit reset, need to reset one more)" -ForegroundColor Yellow
|
||||
Write-Host "[HINT] Use: git reset HEAD~1 (or git reset --mixed HEAD~1)" -ForegroundColor Yellow
|
||||
$allChecksPassed = $false
|
||||
} else {
|
||||
Write-Host "[FAIL] Expected 3 commits, found $commitCount" -ForegroundColor Red
|
||||
Write-Host "[HINT] Use: git reset --mixed HEAD~2 to remove both bad commits" -ForegroundColor Yellow
|
||||
$allChecksPassed = $false
|
||||
}
|
||||
|
||||
# Check that there are NO staged changes
|
||||
$stagedChanges = git diff --cached --name-only 2>$null
|
||||
if (-not $stagedChanges) {
|
||||
Write-Host "[PASS] No staged changes (--mixed unstages everything)" -ForegroundColor Green
|
||||
} else {
|
||||
Write-Host "[FAIL] Found staged changes (--mixed should unstage)" -ForegroundColor Red
|
||||
Write-Host "[HINT] After --mixed reset, changes should be unstaged" -ForegroundColor Yellow
|
||||
$allChecksPassed = $false
|
||||
}
|
||||
|
||||
# Check that there ARE unstaged changes in working directory
|
||||
$unstagedChanges = git diff --name-only 2>$null
|
||||
if ($unstagedChanges) {
|
||||
Write-Host "[PASS] Unstaged changes present in working directory" -ForegroundColor Green
|
||||
|
||||
# Verify unstaged changes contain the experimental/debug code
|
||||
$workingContent = git diff 2>$null
|
||||
if ($workingContent -match "experimental|DEBUG") {
|
||||
Write-Host "[PASS] Unstaged changes contain the reset code" -ForegroundColor Green
|
||||
} else {
|
||||
Write-Host "[INFO] Unstaged changes don't contain expected code" -ForegroundColor Yellow
|
||||
}
|
||||
} else {
|
||||
Write-Host "[FAIL] No unstaged changes found" -ForegroundColor Red
|
||||
Write-Host "[HINT] After --mixed reset, changes should be in working directory (unstaged)" -ForegroundColor Yellow
|
||||
$allChecksPassed = $false
|
||||
}
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# SCENARIO 3: Hard Reset Verification
|
||||
# ============================================================================
|
||||
Write-Host "`n=== Scenario 3: Hard Reset ===`n" -ForegroundColor Cyan
|
||||
|
||||
git switch hard-reset 2>&1 | Out-Null
|
||||
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
Write-Host "[FAIL] hard-reset branch not found" -ForegroundColor Red
|
||||
$allChecksPassed = $false
|
||||
} else {
|
||||
# Count commits (should be 3: Initial + utilities + helper C, bad commit removed)
|
||||
$commitCount = [int](git rev-list --count HEAD 2>$null)
|
||||
|
||||
if ($commitCount -eq 3) {
|
||||
Write-Host "[PASS] Commit count is 3 (broken commit was reset)" -ForegroundColor Green
|
||||
} else {
|
||||
Write-Host "[FAIL] Expected 3 commits, found $commitCount" -ForegroundColor Red
|
||||
Write-Host "[HINT] Use: git reset --hard HEAD~1" -ForegroundColor Yellow
|
||||
$allChecksPassed = $false
|
||||
}
|
||||
|
||||
# Check that there are NO staged changes
|
||||
$stagedChanges = git diff --cached --name-only 2>$null
|
||||
if (-not $stagedChanges) {
|
||||
Write-Host "[PASS] No staged changes (--hard discards everything)" -ForegroundColor Green
|
||||
} else {
|
||||
Write-Host "[FAIL] Found staged changes (--hard should discard all)" -ForegroundColor Red
|
||||
$allChecksPassed = $false
|
||||
}
|
||||
|
||||
# Check that there are NO unstaged changes
|
||||
$unstagedChanges = git diff --name-only 2>$null
|
||||
if (-not $unstagedChanges) {
|
||||
Write-Host "[PASS] No unstaged changes (--hard discards everything)" -ForegroundColor Green
|
||||
} else {
|
||||
Write-Host "[FAIL] Found unstaged changes (--hard should discard all)" -ForegroundColor Red
|
||||
$allChecksPassed = $false
|
||||
}
|
||||
|
||||
# Check working directory is clean
|
||||
$statusOutput = git status --porcelain 2>$null
|
||||
if (-not $statusOutput) {
|
||||
Write-Host "[PASS] Working directory is completely clean" -ForegroundColor Green
|
||||
} else {
|
||||
Write-Host "[INFO] Working directory has some changes" -ForegroundColor Yellow
|
||||
}
|
||||
|
||||
# Verify the file doesn't have the broken code
|
||||
if (Test-Path "utils.py") {
|
||||
$utilsContent = Get-Content "utils.py" -Raw
|
||||
if ($utilsContent -notmatch "helper_d") {
|
||||
Write-Host "[PASS] Broken helper_d function is gone" -ForegroundColor Green
|
||||
} else {
|
||||
Write-Host "[FAIL] Broken helper_d still exists (wasn't reset)" -ForegroundColor Red
|
||||
$allChecksPassed = $false
|
||||
}
|
||||
|
||||
if ($utilsContent -match "helper_c") {
|
||||
Write-Host "[PASS] Good helper_c function is preserved" -ForegroundColor Green
|
||||
} else {
|
||||
Write-Host "[FAIL] Good helper_c function missing" -ForegroundColor Red
|
||||
$allChecksPassed = $false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Set-Location $originalDir
|
||||
|
||||
# Final summary
|
||||
Write-Host ""
|
||||
if ($allChecksPassed) {
|
||||
Write-Host "==========================================" -ForegroundColor Green
|
||||
Write-Host " CONGRATULATIONS! ALL SCENARIOS PASSED!" -ForegroundColor Green
|
||||
Write-Host "==========================================" -ForegroundColor Green
|
||||
Write-Host "`nYou've mastered git reset!" -ForegroundColor Cyan
|
||||
Write-Host "You now understand:" -ForegroundColor Cyan
|
||||
Write-Host " ✓ Resetting commits with --soft (keep staged)" -ForegroundColor White
|
||||
Write-Host " ✓ Resetting commits with --mixed (unstage)" -ForegroundColor White
|
||||
Write-Host " ✓ Resetting commits with --hard (discard all)" -ForegroundColor White
|
||||
Write-Host " ✓ The DANGER of reset on shared history" -ForegroundColor White
|
||||
Write-Host "`n⚠️ CRITICAL REMINDER ⚠️" -ForegroundColor Red
|
||||
Write-Host "NEVER use 'git reset' on commits you've already PUSHED!" -ForegroundColor Red
|
||||
Write-Host "Always use 'git revert' (Module 05) for shared commits!" -ForegroundColor Yellow
|
||||
Write-Host "`nReady for Module 07: Git Stash!" -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 "[REMINDER] Reset is ONLY for local, un-pushed commits!" -ForegroundColor Yellow
|
||||
Write-Host ""
|
||||
exit 1
|
||||
}
|
||||
@@ -1372,12 +1372,12 @@ You've completed this module when you can check off ALL of these:
|
||||
|
||||
Continue your Git journey with advanced techniques:
|
||||
|
||||
- **02_advanced/01-rebasing**: Learn to rebase instead of merge for cleaner history
|
||||
- **02_advanced/02-interactive-rebase**: Clean up messy commits before submitting PRs
|
||||
- **02_advanced/03-worktrees**: Work on multiple branches simultaneously
|
||||
- **02_advanced/04-bisect**: Find bugs using binary search through commit history
|
||||
- **02_advanced/05-blame**: Investigate who changed what and when
|
||||
- **02_advanced/06-merge-strategies**: Master different merge strategies and when to use them
|
||||
- **02-advanced/01-rebasing**: Learn to rebase instead of merge for cleaner history
|
||||
- **02-advanced/02-interactive-rebase**: Clean up messy commits before submitting PRs
|
||||
- **02-advanced/03-worktrees**: Work on multiple branches simultaneously
|
||||
- **02-advanced/04-bisect**: Find bugs using binary search through commit history
|
||||
- **02-advanced/05-blame**: Investigate who changed what and when
|
||||
- **02-advanced/06-merge-strategies**: Master different merge strategies and when to use them
|
||||
|
||||
### Practice More
|
||||
|
||||
@@ -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
|
||||
@@ -1,198 +0,0 @@
|
||||
# Module 10: Reset vs Revert
|
||||
|
||||
## Learning Objectives
|
||||
|
||||
By the end of this module, you will:
|
||||
- Understand the difference between `git reset` and `git revert`
|
||||
- Know when to use reset vs revert
|
||||
- Understand the three modes of reset (--soft, --mixed, --hard)
|
||||
- Safely undo commits in both local and shared branches
|
||||
- Understand the risks of rewriting history
|
||||
|
||||
## Challenge Description
|
||||
|
||||
You have two branches with problematic commits:
|
||||
1. **local-feature**: A private branch with bad commits that you haven't shared with anyone
|
||||
2. **shared-feature**: A branch that has been pushed and others might be using
|
||||
|
||||
Your task is to:
|
||||
1. Use `git reset` to remove the bad commit from the local-feature branch (safe because it's not shared)
|
||||
2. Use `git revert` to undo the bad commit from the shared-feature branch (safe because it preserves history)
|
||||
|
||||
## Key Concepts
|
||||
|
||||
### Git Reset: Rewriting History
|
||||
|
||||
`git reset` moves the branch pointer backward, effectively erasing commits from history. It has three modes:
|
||||
|
||||
**--soft**: Moves HEAD, keeps changes staged
|
||||
```bash
|
||||
git reset --soft HEAD~1
|
||||
# Commit is gone, but changes are staged and ready to commit again
|
||||
```
|
||||
|
||||
**--mixed** (default): Moves HEAD, keeps changes unstaged
|
||||
```bash
|
||||
git reset HEAD~1
|
||||
# Commit is gone, changes are in working directory but not staged
|
||||
```
|
||||
|
||||
**--hard**: Moves HEAD, discards all changes
|
||||
```bash
|
||||
git reset --hard HEAD~1
|
||||
# Commit is gone, changes are PERMANENTLY DELETED
|
||||
```
|
||||
|
||||
### Git Revert: Safe Undo
|
||||
|
||||
`git revert` creates a NEW commit that undoes the changes from a previous commit. History is preserved.
|
||||
|
||||
```bash
|
||||
git revert <commit-hash>
|
||||
# Creates a new commit that reverses the specified commit
|
||||
```
|
||||
|
||||
### Visual Comparison
|
||||
|
||||
**Before (both branches):**
|
||||
```
|
||||
A---B---C---D (D is the bad commit)
|
||||
```
|
||||
|
||||
**After Reset (rewrites history):**
|
||||
```
|
||||
A---B---C
|
||||
```
|
||||
Commit D is gone. If anyone else had D, they'll have problems.
|
||||
|
||||
**After Revert (preserves history):**
|
||||
```
|
||||
A---B---C---D---E
|
||||
```
|
||||
E is a new commit that undoes D. Everyone can pull E safely.
|
||||
|
||||
### When to Use Each
|
||||
|
||||
**Use Reset when:**
|
||||
- The commits haven't been pushed to a shared repository
|
||||
- You're cleaning up local commits before pushing
|
||||
- You made a mistake locally and want to start over
|
||||
- You're working alone on a branch
|
||||
|
||||
**Use Revert when:**
|
||||
- The commits have been pushed to a shared repository
|
||||
- Others might have based work on these commits
|
||||
- You want to preserve the complete history
|
||||
- You need a safe, reversible undo operation
|
||||
|
||||
### The Golden Rule
|
||||
|
||||
**Never use `git reset` on commits that have been pushed to a shared branch!**
|
||||
|
||||
This will cause problems for anyone who has pulled those commits. Use `git revert` instead.
|
||||
|
||||
## Useful Commands
|
||||
|
||||
```bash
|
||||
# Reset (for local-only commits)
|
||||
git reset --soft HEAD~1 # Undo commit, keep changes staged
|
||||
git reset HEAD~1 # Undo commit, keep changes unstaged
|
||||
git reset --hard HEAD~1 # Undo commit, discard changes (DANGEROUS!)
|
||||
|
||||
# Reset to a specific commit
|
||||
git reset --hard <commit-hash>
|
||||
|
||||
# Revert (for shared commits)
|
||||
git revert <commit-hash>
|
||||
git revert HEAD # Revert the last commit
|
||||
|
||||
# See what would be affected before resetting
|
||||
git log --oneline
|
||||
git diff HEAD~1
|
||||
|
||||
# If you reset by mistake, you can sometimes recover with reflog
|
||||
git reflog
|
||||
git reset --hard <commit-hash-from-reflog>
|
||||
```
|
||||
|
||||
## Verification
|
||||
|
||||
Run the verification script to check your solution:
|
||||
|
||||
```bash
|
||||
.\verify.ps1
|
||||
```
|
||||
|
||||
The verification will check that:
|
||||
- local-feature branch has the bad commit removed via reset
|
||||
- shared-feature branch has the bad commit undone via revert
|
||||
- shared-feature has a revert commit in the history
|
||||
- All good commits are preserved
|
||||
|
||||
## Challenge Steps
|
||||
|
||||
1. Navigate to the challenge directory
|
||||
2. You're on the local-feature branch with a bad commit
|
||||
3. View commits: `git log --oneline`
|
||||
4. Use `git reset --hard HEAD~1` to remove the bad commit
|
||||
5. Switch to shared-feature: `git switch shared-feature`
|
||||
6. View commits: `git log --oneline`
|
||||
7. Find the hash of the "Add broken feature" commit
|
||||
8. Use `git revert <commit-hash>` to undo it safely
|
||||
9. Run the verification script
|
||||
|
||||
## Tips
|
||||
|
||||
- `HEAD~1` means "one commit before HEAD"
|
||||
- `HEAD~2` means "two commits before HEAD"
|
||||
- Always check `git log` before and after reset/revert
|
||||
- `git reset --hard` is DANGEROUS - it permanently deletes uncommitted changes
|
||||
- If you're unsure, use `git reset --soft` instead of `--hard`
|
||||
- Revert will open an editor for the commit message - you can accept the default
|
||||
- You can always use `.\reset.ps1` to start over if you make a mistake
|
||||
|
||||
## Common Mistakes to Avoid
|
||||
|
||||
### Mistake 1: Using Reset on Pushed Commits
|
||||
```bash
|
||||
# DON'T DO THIS if the commit was pushed!
|
||||
git reset --hard HEAD~1
|
||||
git push --force # This will cause problems for others
|
||||
```
|
||||
|
||||
### Mistake 2: Using --hard Without Checking
|
||||
```bash
|
||||
# This DELETES your work permanently!
|
||||
git reset --hard HEAD~1 # Uncommitted changes are GONE
|
||||
```
|
||||
|
||||
### Mistake 3: Reverting the Wrong Commit
|
||||
```bash
|
||||
# Always double-check the commit hash
|
||||
git log --oneline
|
||||
git show <commit-hash> # Verify it's the right commit
|
||||
git revert <commit-hash> # Now revert it
|
||||
```
|
||||
|
||||
## Recovery from Mistakes
|
||||
|
||||
If you reset by accident, Git keeps a reflog:
|
||||
|
||||
```bash
|
||||
# See recent HEAD movements
|
||||
git reflog
|
||||
|
||||
# Find the commit you want to restore
|
||||
# Output looks like:
|
||||
# abc1234 HEAD@{0}: reset: moving to HEAD~1
|
||||
# def5678 HEAD@{1}: commit: The commit you just lost
|
||||
|
||||
# Restore it
|
||||
git reset --hard def5678
|
||||
```
|
||||
|
||||
The reflog is your safety net, but it only keeps history for about 30 days.
|
||||
|
||||
## What You'll Learn
|
||||
|
||||
Understanding when to use reset versus revert is crucial for safe Git usage. Reset is powerful but dangerous when used on shared commits, while revert is always safe but creates additional history. Mastering both commands and knowing which to use in different situations is a hallmark of Git expertise. The rule is simple: if in doubt, use revert - it's always safe.
|
||||
@@ -1,22 +0,0 @@
|
||||
#!/usr/bin/env pwsh
|
||||
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Resets the reset vs revert 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,190 +0,0 @@
|
||||
#!/usr/bin/env pwsh
|
||||
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Sets up the reset vs revert challenge environment.
|
||||
|
||||
.DESCRIPTION
|
||||
Creates a Git repository with two branches:
|
||||
- local-feature: A private branch where reset should be used
|
||||
- shared-feature: A pushed branch where revert should be used
|
||||
#>
|
||||
|
||||
# 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 commits on main
|
||||
$app = @"
|
||||
class Calculator:
|
||||
def add(self, a, b):
|
||||
return a + b
|
||||
|
||||
def subtract(self, a, b):
|
||||
return a - b
|
||||
"@
|
||||
|
||||
Set-Content -Path "calculator.py" -Value $app
|
||||
git add calculator.py
|
||||
git commit -m "Initial calculator implementation" | Out-Null
|
||||
|
||||
$readme = @"
|
||||
# Calculator App
|
||||
|
||||
A simple calculator application.
|
||||
"@
|
||||
|
||||
Set-Content -Path "README.md" -Value $readme
|
||||
git add README.md
|
||||
git commit -m "Add README" | Out-Null
|
||||
|
||||
# Create local-feature branch (private, not shared)
|
||||
git checkout -b local-feature | Out-Null
|
||||
|
||||
$appWithMultiply = @"
|
||||
class Calculator:
|
||||
def add(self, a, b):
|
||||
return a + b
|
||||
|
||||
def subtract(self, a, b):
|
||||
return a - b
|
||||
|
||||
def multiply(self, a, b):
|
||||
return a * b
|
||||
"@
|
||||
|
||||
Set-Content -Path "calculator.py" -Value $appWithMultiply
|
||||
git add calculator.py
|
||||
git commit -m "Add multiply function" | Out-Null
|
||||
|
||||
# Add a bad commit that should be removed with reset
|
||||
$appWithBadCode = @"
|
||||
class Calculator:
|
||||
def add(self, a, b):
|
||||
return a + b
|
||||
|
||||
def subtract(self, a, b):
|
||||
return a - b
|
||||
|
||||
def multiply(self, a, b):
|
||||
return a * b
|
||||
|
||||
# BUG: This is broken and should never have been committed!
|
||||
def divide(self, a, b):
|
||||
# Forgot to check for division by zero
|
||||
return a / b # This will raise ZeroDivisionError for zero!
|
||||
"@
|
||||
|
||||
Set-Content -Path "calculator.py" -Value $appWithBadCode
|
||||
git add calculator.py
|
||||
git commit -m "Add broken divide function - DO NOT KEEP" | Out-Null
|
||||
|
||||
# Switch back to main for shared-feature branch
|
||||
git checkout main | Out-Null
|
||||
|
||||
# Create shared-feature branch (simulating a pushed/shared branch)
|
||||
git checkout -b shared-feature | Out-Null
|
||||
|
||||
$appWithPower = @"
|
||||
class Calculator:
|
||||
def add(self, a, b):
|
||||
return a + b
|
||||
|
||||
def subtract(self, a, b):
|
||||
return a - b
|
||||
|
||||
def power(self, a, b):
|
||||
return a ** b
|
||||
"@
|
||||
|
||||
Set-Content -Path "calculator.py" -Value $appWithPower
|
||||
git add calculator.py
|
||||
git commit -m "Add power function" | Out-Null
|
||||
|
||||
# Add a bad commit that should be reverted (not reset)
|
||||
$appWithBrokenFeature = @"
|
||||
import math
|
||||
|
||||
class Calculator:
|
||||
def add(self, a, b):
|
||||
return a + b
|
||||
|
||||
def subtract(self, a, b):
|
||||
return a - b
|
||||
|
||||
def power(self, a, b):
|
||||
return a ** b
|
||||
|
||||
# BUG: This breaks the calculator!
|
||||
def square_root(self, a):
|
||||
# This implementation is wrong for negative numbers
|
||||
return math.sqrt(a) # Raises ValueError for negative numbers without warning!
|
||||
"@
|
||||
|
||||
Set-Content -Path "calculator.py" -Value $appWithBrokenFeature
|
||||
git add calculator.py
|
||||
git commit -m "Add broken feature" | Out-Null
|
||||
|
||||
# Add another good commit after the bad one (to show that revert preserves subsequent commits)
|
||||
$appWithMoreFeatures = @"
|
||||
import math
|
||||
|
||||
class Calculator:
|
||||
def add(self, a, b):
|
||||
return a + b
|
||||
|
||||
def subtract(self, a, b):
|
||||
return a - b
|
||||
|
||||
def power(self, a, b):
|
||||
return a ** b
|
||||
|
||||
# BUG: This breaks the calculator!
|
||||
def square_root(self, a):
|
||||
# This implementation is wrong for negative numbers
|
||||
return math.sqrt(a) # Raises ValueError for negative numbers without warning!
|
||||
|
||||
def modulo(self, a, b):
|
||||
return a % b
|
||||
"@
|
||||
|
||||
Set-Content -Path "calculator.py" -Value $appWithMoreFeatures
|
||||
git add calculator.py
|
||||
git commit -m "Add modulo function" | Out-Null
|
||||
|
||||
# Switch to local-feature for the challenge start
|
||||
git checkout local-feature | 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 have two branches with bad commits:" -ForegroundColor Cyan
|
||||
Write-Host "`n1. local-feature (PRIVATE - not shared):" -ForegroundColor Yellow
|
||||
Write-Host " - Has a broken divide function commit" -ForegroundColor White
|
||||
Write-Host " - Safe to use 'git reset' to remove it" -ForegroundColor Green
|
||||
Write-Host "`n2. shared-feature (PUBLIC - shared with team):" -ForegroundColor Yellow
|
||||
Write-Host " - Has a broken feature commit" -ForegroundColor White
|
||||
Write-Host " - Must use 'git revert' to undo it safely" -ForegroundColor Green
|
||||
Write-Host "`nYour task:" -ForegroundColor Yellow
|
||||
Write-Host "1. Navigate to the challenge directory: cd challenge" -ForegroundColor White
|
||||
Write-Host "2. You're on local-feature - view commits: git log --oneline" -ForegroundColor White
|
||||
Write-Host "3. Remove the bad commit with: git reset --hard HEAD~1" -ForegroundColor White
|
||||
Write-Host "4. Switch to shared-feature: git checkout shared-feature" -ForegroundColor White
|
||||
Write-Host "5. Find the 'Add broken feature' commit hash: git log --oneline" -ForegroundColor White
|
||||
Write-Host "6. Revert it with: git revert <commit-hash>" -ForegroundColor White
|
||||
Write-Host "`nRun '../verify.ps1' from the challenge directory to check your solution.`n" -ForegroundColor Cyan
|
||||
@@ -1,172 +0,0 @@
|
||||
#!/usr/bin/env pwsh
|
||||
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Verifies the reset vs revert challenge solution.
|
||||
|
||||
.DESCRIPTION
|
||||
Checks that the user correctly used reset on the local branch
|
||||
and revert on the shared branch.
|
||||
#>
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
# Verify local-feature branch
|
||||
Write-Host "`nChecking local-feature branch..." -ForegroundColor Cyan
|
||||
git checkout local-feature 2>$null | Out-Null
|
||||
|
||||
# Check commit count on local-feature (should be 3: initial + README + multiply)
|
||||
$localCommitCount = (git rev-list --count local-feature 2>$null)
|
||||
if ($localCommitCount -ne 3) {
|
||||
Write-Host "[FAIL] local-feature should have 3 commits, found $localCommitCount" -ForegroundColor Red
|
||||
if ($localCommitCount -gt 3) {
|
||||
Write-Host "Hint: The bad commit should be removed. Use 'git reset --hard HEAD~1'" -ForegroundColor Yellow
|
||||
} else {
|
||||
Write-Host "Hint: You may have reset too far. Run ../reset.ps1 to start over." -ForegroundColor Yellow
|
||||
}
|
||||
Set-Location ..
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Check that calculator.py exists
|
||||
if (-not (Test-Path "calculator.py")) {
|
||||
Write-Host "[FAIL] calculator.py not found." -ForegroundColor Red
|
||||
Set-Location ..
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Check calculator.py on local-feature
|
||||
$localCalcContent = Get-Content "calculator.py" -Raw
|
||||
|
||||
# Should have multiply function
|
||||
if ($localCalcContent -notmatch "multiply") {
|
||||
Write-Host "[FAIL] calculator.py should have the multiply function." -ForegroundColor Red
|
||||
Set-Location ..
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Should NOT have divide function (it was in the bad commit that should be reset)
|
||||
if ($localCalcContent -match "divide") {
|
||||
Write-Host "[FAIL] calculator.py should NOT have the divide function." -ForegroundColor Red
|
||||
Write-Host "Hint: Use 'git reset --hard HEAD~1' to remove the bad commit" -ForegroundColor Yellow
|
||||
Set-Location ..
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Check commit messages on local-feature
|
||||
$localCommits = git log --pretty=format:"%s" local-feature 2>$null
|
||||
if ($localCommits -match "broken divide") {
|
||||
Write-Host "[FAIL] The 'broken divide' commit should be removed from local-feature." -ForegroundColor Red
|
||||
Write-Host "Hint: Use 'git reset --hard HEAD~1' to remove it" -ForegroundColor Yellow
|
||||
Set-Location ..
|
||||
exit 1
|
||||
}
|
||||
|
||||
Write-Host "[PASS] local-feature branch correctly reset!" -ForegroundColor Green
|
||||
|
||||
# Verify shared-feature branch
|
||||
Write-Host "`nChecking shared-feature branch..." -ForegroundColor Cyan
|
||||
git checkout shared-feature 2>$null | Out-Null
|
||||
|
||||
# Check commit count on shared-feature
|
||||
# Should be 6: initial + README + power + broken feature + modulo + revert
|
||||
$sharedCommitCount = (git rev-list --count shared-feature 2>$null)
|
||||
if ($sharedCommitCount -ne 6) {
|
||||
Write-Host "[FAIL] shared-feature should have 6 commits, found $sharedCommitCount" -ForegroundColor Red
|
||||
if ($sharedCommitCount -lt 6) {
|
||||
Write-Host "Hint: You should REVERT the bad commit, not reset it." -ForegroundColor Yellow
|
||||
Write-Host " Revert creates a new commit that undoes the bad one." -ForegroundColor Yellow
|
||||
Write-Host " Use: git revert <commit-hash>" -ForegroundColor Yellow
|
||||
} else {
|
||||
Write-Host "Hint: You should have exactly 6 commits after reverting." -ForegroundColor Yellow
|
||||
}
|
||||
Set-Location ..
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Check that there's a revert commit
|
||||
$sharedCommits = git log --pretty=format:"%s" shared-feature 2>$null
|
||||
if ($sharedCommits -notmatch "Revert") {
|
||||
Write-Host "[FAIL] No revert commit found on shared-feature." -ForegroundColor Red
|
||||
Write-Host "Hint: Use 'git revert <commit-hash>' to undo the bad commit" -ForegroundColor Yellow
|
||||
Write-Host " Find the hash with: git log --oneline" -ForegroundColor Yellow
|
||||
Set-Location ..
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Check calculator.py on shared-feature
|
||||
$sharedCalcContent = Get-Content "calculator.py" -Raw
|
||||
|
||||
# Should have power function
|
||||
if ($sharedCalcContent -notmatch "power") {
|
||||
Write-Host "[FAIL] calculator.py should have the power function." -ForegroundColor Red
|
||||
Set-Location ..
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Should have modulo function (commits after the reverted one should be preserved)
|
||||
if ($sharedCalcContent -notmatch "modulo") {
|
||||
Write-Host "[FAIL] calculator.py should have the modulo function." -ForegroundColor Red
|
||||
Write-Host "Hint: Reverting should preserve commits made after the bad one" -ForegroundColor Yellow
|
||||
Set-Location ..
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Should NOT have square_root function (it was in the bad commit that should be reverted)
|
||||
if ($sharedCalcContent -match "square_root") {
|
||||
Write-Host "[FAIL] calculator.py should NOT have the square_root function." -ForegroundColor Red
|
||||
Write-Host "Hint: The 'Add broken feature' commit should be reverted" -ForegroundColor Yellow
|
||||
Write-Host " Use: git revert <commit-hash-of-broken-feature>" -ForegroundColor Yellow
|
||||
Set-Location ..
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Verify the revert commit specifically reverted the "Add broken feature" commit
|
||||
$revertCommitMessage = git log --grep="Revert" --pretty=format:"%s" -n 1 2>$null
|
||||
if ($revertCommitMessage -notmatch "broken feature") {
|
||||
Write-Host "[FAIL] The revert commit should mention 'broken feature'." -ForegroundColor Red
|
||||
Write-Host "Hint: Make sure you reverted the correct commit (the one that added square_root)" -ForegroundColor Yellow
|
||||
Set-Location ..
|
||||
exit 1
|
||||
}
|
||||
|
||||
Write-Host "[PASS] shared-feature branch correctly reverted!" -ForegroundColor Green
|
||||
|
||||
# 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 "- Used 'git reset' on local-feature (private branch)" -ForegroundColor White
|
||||
Write-Host " Removed the bad commit completely from history" -ForegroundColor White
|
||||
Write-Host "- Used 'git revert' on shared-feature (public branch)" -ForegroundColor White
|
||||
Write-Host " Created a new commit that undoes the bad one" -ForegroundColor White
|
||||
Write-Host " Preserved all history and subsequent commits" -ForegroundColor White
|
||||
Write-Host "`nYou now understand when to use reset vs revert!" -ForegroundColor Green
|
||||
Write-Host "`nKey takeaway:" -ForegroundColor Yellow
|
||||
Write-Host "- Reset rewrites history (use only on private commits)" -ForegroundColor White
|
||||
Write-Host "- Revert preserves history (safe for shared commits)`n" -ForegroundColor White
|
||||
|
||||
Set-Location ..
|
||||
exit 0
|
||||
@@ -74,7 +74,7 @@ Use bisect when:
|
||||
|
||||
## Useful Commands
|
||||
|
||||
```bash
|
||||
```powershell
|
||||
# Start bisect session
|
||||
git bisect start
|
||||
|
||||
@@ -112,7 +112,7 @@ git bisect log
|
||||
|
||||
Run the verification script to check your solution:
|
||||
|
||||
```bash
|
||||
```powershell
|
||||
.\verify.ps1
|
||||
```
|
||||
|
||||
@@ -152,7 +152,7 @@ The verification will check that:
|
||||
## Manual vs Automated Bisect
|
||||
|
||||
### Manual Bisect
|
||||
```bash
|
||||
```powershell
|
||||
git bisect start
|
||||
git bisect bad
|
||||
git bisect good HEAD~20
|
||||
@@ -165,7 +165,7 @@ git bisect reset
|
||||
```
|
||||
|
||||
### Automated Bisect
|
||||
```bash
|
||||
```powershell
|
||||
git bisect start
|
||||
git bisect bad
|
||||
git bisect good HEAD~20
|
||||
@@ -182,22 +182,28 @@ The test script should exit with:
|
||||
## Bisect Workflow Example
|
||||
|
||||
### Finding a Performance Regression
|
||||
```bash
|
||||
```powershell
|
||||
# App is slow now, was fast 50 commits ago
|
||||
git bisect start
|
||||
git bisect bad
|
||||
git bisect good HEAD~50
|
||||
|
||||
# Create test script
|
||||
echo '#!/bin/bash\ntime npm start | grep "Started in"' > test.sh
|
||||
chmod +x test.sh
|
||||
@'
|
||||
$output = npm start 2>&1 | Select-String "Started in"
|
||||
if ($output -match "Started in (\d+)") {
|
||||
if ([int]$Matches[1] -gt 5000) { exit 1 } # Slow
|
||||
else { exit 0 } # Fast
|
||||
}
|
||||
exit 1
|
||||
'@ | Out-File -FilePath test.ps1
|
||||
|
||||
git bisect run ./test.sh
|
||||
git bisect run pwsh test.ps1
|
||||
# Git finds the commit that made it slow
|
||||
```
|
||||
|
||||
### Finding When a Feature Broke
|
||||
```bash
|
||||
```powershell
|
||||
git bisect start
|
||||
git bisect bad
|
||||
git bisect good v1.0.0 # Last known good version
|
||||
@@ -247,6 +247,16 @@ git revert --abort
|
||||
```
|
||||
Abort a revert in progress.
|
||||
|
||||
```bash
|
||||
git revert -m 1 <merge-commit>
|
||||
```
|
||||
Revert a merge commit (requires -m flag to specify which parent to keep). Use `-m 1` to keep the branch you merged into (most common).
|
||||
|
||||
```bash
|
||||
git revert -m 2 <merge-commit>
|
||||
```
|
||||
Revert a merge commit but keep the branch that was merged in (rare).
|
||||
|
||||
### Cherry-Pick
|
||||
|
||||
```bash
|
||||
|
||||
337
INSTALLATION.md
Normal file
337
INSTALLATION.md
Normal file
@@ -0,0 +1,337 @@
|
||||
# Installation Guide for Windows 11
|
||||
|
||||
This guide will help you install everything needed for the Git Workshop on Windows 11.
|
||||
|
||||
## Quick Start (Automated Installation)
|
||||
|
||||
**Easiest option:** Run our oneshot installation script that installs all prerequisites and clones the repository automatically.
|
||||
|
||||
```powershell
|
||||
irm https://git.frod.dk/floppydiscen/git-workshop/raw/branch/main/install.ps1 | iex
|
||||
```
|
||||
|
||||
This will:
|
||||
- Install PowerShell 7, Git 2.23+, and Visual Studio Code
|
||||
- Clone the git-workshop repository to `~/git-workshop`
|
||||
- Leave you ready to start the workshop immediately
|
||||
|
||||
**Alternative:** If you've already cloned the repository, you can run the local installation script:
|
||||
|
||||
1. Open **PowerShell** or **Windows Terminal**
|
||||
2. Navigate to the git-workshop directory
|
||||
3. Run the installation script:
|
||||
|
||||
```powershell
|
||||
.\install-prerequisites.ps1
|
||||
```
|
||||
|
||||
The script will:
|
||||
- Check if tools are already installed
|
||||
- Install PowerShell 7, Git 2.23+, and Visual Studio Code
|
||||
- Prompt you for optional tools (Python 3.12, Windows Terminal)
|
||||
- Show clear progress and verify each installation
|
||||
- Display Git configuration instructions when complete
|
||||
|
||||
**If you prefer manual installation**, continue with the detailed steps below.
|
||||
|
||||
---
|
||||
|
||||
## Prerequisites
|
||||
|
||||
You'll need administrator access to install software on your Windows 11 machine.
|
||||
|
||||
## What You'll Install
|
||||
|
||||
1. **PowerShell 7** - Modern cross-platform PowerShell (replaces the older Windows PowerShell 5.1)
|
||||
2. **Git** - Version control system (2.23 or later)
|
||||
3. **Visual Studio Code** - Modern code editor with excellent Git integration
|
||||
|
||||
## Manual Installation Steps
|
||||
|
||||
### 1. Install PowerShell 7
|
||||
|
||||
PowerShell 7 is the modern, cross-platform version of PowerShell. Windows 11 comes with PowerShell 5.1, but we recommend PowerShell 7 for the best experience.
|
||||
|
||||
**Option A: Using winget (Recommended)**
|
||||
|
||||
Open **Windows Terminal** or **Command Prompt** and run:
|
||||
|
||||
```powershell
|
||||
winget install --id Microsoft.PowerShell --source winget
|
||||
```
|
||||
|
||||
**Option B: Manual Download**
|
||||
|
||||
1. Visit https://github.com/PowerShell/PowerShell/releases/latest
|
||||
2. Download the file ending in `-win-x64.msi` (e.g., `PowerShell-7.4.1-win-x64.msi`)
|
||||
3. Run the installer
|
||||
4. Accept all defaults
|
||||
|
||||
**Verify Installation:**
|
||||
|
||||
Open a new terminal and run:
|
||||
|
||||
```powershell
|
||||
pwsh --version
|
||||
```
|
||||
|
||||
You should see version 7.x.x or higher.
|
||||
|
||||
**Important:** After installing PowerShell 7, use it instead of the older "Windows PowerShell 5.1". Look for "PowerShell 7" in your Start menu or Windows Terminal.
|
||||
|
||||
### 2. Install Git
|
||||
|
||||
Git is the version control system you'll learn in this workshop. You need version 2.23 or later.
|
||||
|
||||
**Option A: Using winget (Recommended)**
|
||||
|
||||
```powershell
|
||||
winget install --id Git.Git -e --source winget
|
||||
```
|
||||
|
||||
**Option B: Manual Download**
|
||||
|
||||
1. Visit https://git-scm.com/downloads
|
||||
2. Click "Windows"
|
||||
3. Download the 64-bit installer
|
||||
4. Run the installer with these recommended settings:
|
||||
- **Default editor**: Choose "Visual Studio Code" (we'll install it next)
|
||||
- **PATH environment**: Select "Git from the command line and also from 3rd-party software"
|
||||
- **Line ending conversions**: Choose "Checkout Windows-style, commit Unix-style line endings"
|
||||
- **Terminal emulator**: Choose "Use Windows' default console window"
|
||||
- All other settings: Accept defaults
|
||||
|
||||
**Verify Installation:**
|
||||
|
||||
Open a **new** PowerShell window and run:
|
||||
|
||||
```powershell
|
||||
git --version
|
||||
```
|
||||
|
||||
You should see version 2.23 or higher (e.g., `git version 2.43.0`).
|
||||
|
||||
### 3. Install Visual Studio Code
|
||||
|
||||
VS Code is a free, powerful code editor with excellent Git integration.
|
||||
|
||||
**Option A: Using winget (Recommended)**
|
||||
|
||||
```powershell
|
||||
winget install --id Microsoft.VisualStudioCode --source winget
|
||||
```
|
||||
|
||||
**Option B: Manual Download**
|
||||
|
||||
1. Visit https://code.visualstudio.com/
|
||||
2. Click "Download for Windows"
|
||||
3. Run the installer
|
||||
4. During installation, check these options:
|
||||
- ✅ Add "Open with Code" action to Windows Explorer file context menu
|
||||
- ✅ Add "Open with Code" action to Windows Explorer directory context menu
|
||||
- ✅ Register Code as an editor for supported file types
|
||||
- ✅ Add to PATH
|
||||
|
||||
**Verify Installation:**
|
||||
|
||||
```powershell
|
||||
code --version
|
||||
```
|
||||
|
||||
You should see version information.
|
||||
|
||||
**Recommended VS Code Extensions:**
|
||||
|
||||
Open VS Code and install these extensions for the best Git experience:
|
||||
|
||||
1. **GitLens** - Supercharge Git capabilities
|
||||
- Press `Ctrl+Shift+X` to open Extensions
|
||||
- Search for "GitLens"
|
||||
- Click Install
|
||||
|
||||
2. **Git Graph** - View Git history visually
|
||||
- Search for "Git Graph"
|
||||
- Click Install
|
||||
|
||||
3. **PowerShell** - Better PowerShell support
|
||||
- Search for "PowerShell"
|
||||
- Install the one from Microsoft
|
||||
|
||||
## Configure Git
|
||||
|
||||
Before making your first commit, tell Git who you are:
|
||||
|
||||
```powershell
|
||||
git config --global user.name "Your Name"
|
||||
git config --global user.email "your.email@example.com"
|
||||
```
|
||||
|
||||
**Verify your configuration:**
|
||||
|
||||
```powershell
|
||||
git config --global user.name
|
||||
git config --global user.email
|
||||
```
|
||||
|
||||
You should see your name and email printed.
|
||||
|
||||
**Optional: Set VS Code as Git's Default Editor**
|
||||
|
||||
If you installed Git before VS Code, configure Git to use VS Code:
|
||||
|
||||
```powershell
|
||||
git config --global core.editor "code --wait"
|
||||
```
|
||||
|
||||
## PowerShell Execution Policy
|
||||
|
||||
When running PowerShell scripts (`.ps1` files) in this workshop, you might encounter an error about execution policies.
|
||||
|
||||
**If you see an error like "script cannot be loaded because running scripts is disabled":**
|
||||
|
||||
Open **PowerShell 7 as Administrator** and run:
|
||||
|
||||
```powershell
|
||||
Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser
|
||||
```
|
||||
|
||||
This allows you to run local scripts while maintaining security for downloaded scripts.
|
||||
|
||||
## Running Scripts in the Workshop
|
||||
|
||||
After installation, you can run workshop scripts using:
|
||||
|
||||
```powershell
|
||||
.\setup.ps1
|
||||
.\verify.ps1
|
||||
.\reset.ps1
|
||||
```
|
||||
|
||||
**Example workflow:**
|
||||
|
||||
```powershell
|
||||
# Navigate to a module
|
||||
cd 01-essentials\01-basics
|
||||
|
||||
# Run the setup script
|
||||
.\setup.ps1
|
||||
|
||||
# Complete the challenge using Git commands
|
||||
# ...
|
||||
|
||||
# Verify your solution
|
||||
.\verify.ps1
|
||||
```
|
||||
|
||||
## Optional: Python (for Module 08 only)
|
||||
|
||||
Module 08 (Multiplayer Git) uses Python for "The Great Print Project". You only need this for that specific module.
|
||||
|
||||
**Install Python 3.12:**
|
||||
|
||||
```powershell
|
||||
winget install --id Python.Python.3.12 --source winget
|
||||
```
|
||||
|
||||
**Verify installation:**
|
||||
|
||||
```powershell
|
||||
python --version
|
||||
```
|
||||
|
||||
You should see Python 3.12.x or higher.
|
||||
|
||||
## Optional: Windows Terminal (Highly Recommended)
|
||||
|
||||
Windows Terminal provides a modern terminal experience with tabs, better colors, and PowerShell 7 integration.
|
||||
|
||||
**Install:**
|
||||
|
||||
```powershell
|
||||
winget install --id Microsoft.WindowsTerminal --source winget
|
||||
```
|
||||
|
||||
Or install from the **Microsoft Store** (search for "Windows Terminal").
|
||||
|
||||
**After installation:**
|
||||
- Press `Win+X` and select "Windows Terminal"
|
||||
- Or search "Terminal" in the Start menu
|
||||
- PowerShell 7 should be the default profile
|
||||
|
||||
## Verify Complete Installation
|
||||
|
||||
Run these commands to verify everything is installed correctly:
|
||||
|
||||
```powershell
|
||||
# PowerShell version (should be 7.x.x)
|
||||
pwsh --version
|
||||
|
||||
# Git version (should be 2.23 or higher)
|
||||
git --version
|
||||
|
||||
# VS Code version
|
||||
code --version
|
||||
|
||||
# Git configuration
|
||||
git config --global user.name
|
||||
git config --global user.email
|
||||
|
||||
# Optional: Python (for Module 08)
|
||||
python --version
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Git command not found
|
||||
|
||||
If `git --version` doesn't work after installation:
|
||||
1. Close and reopen your terminal (Git needs a new terminal to update PATH)
|
||||
2. Restart your computer if the problem persists
|
||||
|
||||
### VS Code command not found
|
||||
|
||||
If `code --version` doesn't work:
|
||||
1. Ensure you checked "Add to PATH" during installation
|
||||
2. Close and reopen your terminal
|
||||
3. If still not working, reinstall VS Code with the PATH option enabled
|
||||
|
||||
### PowerShell execution policy errors
|
||||
|
||||
If you can't run `.ps1` scripts:
|
||||
1. Open PowerShell 7 **as Administrator**
|
||||
2. Run: `Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser`
|
||||
3. Close admin PowerShell and try again in a regular PowerShell window
|
||||
|
||||
### winget command not found
|
||||
|
||||
If `winget` doesn't work:
|
||||
1. Update Windows 11 to the latest version (Settings → Windows Update)
|
||||
2. Install "App Installer" from the Microsoft Store
|
||||
3. Restart your computer
|
||||
|
||||
## You're Ready!
|
||||
|
||||
Once all verification commands work, you're ready to start the workshop!
|
||||
|
||||
```powershell
|
||||
# Clone or download the git-workshop repository
|
||||
# Navigate to it
|
||||
cd path\to\git-workshop
|
||||
|
||||
# Start with Module 01
|
||||
cd 01-essentials\01-basics
|
||||
|
||||
# Read the instructions
|
||||
code README.md
|
||||
|
||||
# Run setup and begin!
|
||||
.\setup.ps1
|
||||
```
|
||||
|
||||
## Next Steps
|
||||
|
||||
- Read the main [README.md](README.md) for workshop overview
|
||||
- Check [GIT-CHEATSHEET.md](GIT-CHEATSHEET.md) for Git command reference
|
||||
- Start with Module 01: `01-essentials\01-basics`
|
||||
|
||||
Happy learning!
|
||||
205
README.md
205
README.md
@@ -8,19 +8,18 @@ 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 (8 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: Git Revert** - Safe undoing - preserve history while reversing changes (includes merge commit reversion)
|
||||
- **Module 06: Git Reset** - Dangerous history rewriting - local cleanup only (NEVER on pushed commits!)
|
||||
- **Module 07: Stash** - Temporarily save work without committing
|
||||
- **Module 08: Multiplayer Git** - **The Great Print Project** - Real cloud-based collaboration with teammates
|
||||
|
||||
### 02 Advanced - Professional Techniques (6 modules)
|
||||
|
||||
@@ -37,7 +36,7 @@ Advanced Git workflows for power users:
|
||||
|
||||
### For Local Modules (01-08 in Essentials, all Advanced modules)
|
||||
|
||||
1. Navigate to a module directory (e.g., `01_essentials/01-basics`)
|
||||
1. Navigate to a module directory (e.g., `01-essentials/01-basics`)
|
||||
2. Read the `README.md` to understand the challenge
|
||||
3. Run `./setup.ps1` to create the challenge environment
|
||||
4. Complete the challenge using git commands
|
||||
@@ -46,24 +45,22 @@ 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 08: Multiplayer Git
|
||||
|
||||
**This module is different!** It uses a real Git server for authentic collaboration:
|
||||
|
||||
1. Navigate to `01_essentials/09-multiplayer`
|
||||
1. Navigate to `01-essentials/08-multiplayer`
|
||||
2. Read the `README.md` for complete instructions
|
||||
3. **No setup script** - you'll clone from https://git.frod.dk/multiplayer
|
||||
4. Work with a partner on shared branches
|
||||
5. Experience real merge conflicts and pull requests
|
||||
6. **No verify script** - success is visual (your code appears in the final output)
|
||||
|
||||
**Facilitators**: See `01_essentials/09-multiplayer/FACILITATOR-SETUP.md` for server setup and workshop guidance.
|
||||
**Facilitators**: See `01-essentials/08-multiplayer/FACILITATOR-SETUP.md` for server setup and workshop guidance.
|
||||
|
||||
## Running PowerShell Scripts
|
||||
|
||||
Most modules include setup, verification, and reset scripts. Here's how to run them:
|
||||
|
||||
### Windows
|
||||
Most modules include setup, verification, and reset scripts.
|
||||
|
||||
If you encounter an "execution policy" error when running scripts, open PowerShell as Administrator and run:
|
||||
|
||||
@@ -74,76 +71,71 @@ Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser
|
||||
Then run scripts using:
|
||||
```powershell
|
||||
.\setup.ps1
|
||||
.\verify.ps1
|
||||
.\reset.ps1
|
||||
```
|
||||
|
||||
### macOS/Linux
|
||||
|
||||
First, make sure scripts are executable (only needed once):
|
||||
```bash
|
||||
chmod +x 01_essentials/*/setup.ps1 01_essentials/*/verify.ps1 01_essentials/*/reset.ps1
|
||||
chmod +x 02_advanced/*/setup.ps1 02_advanced/*/verify.ps1 02_advanced/*/reset.ps1
|
||||
```
|
||||
|
||||
Then run scripts using:
|
||||
```bash
|
||||
./setup.ps1
|
||||
```
|
||||
|
||||
Or explicitly with PowerShell:
|
||||
```bash
|
||||
pwsh setup.ps1
|
||||
```
|
||||
|
||||
**Note:** All examples in this workshop use Windows syntax (`.\script.ps1`). macOS/Linux users should use `./script.ps1` instead.
|
||||
|
||||
## Requirements
|
||||
|
||||
### Git
|
||||
### Installation
|
||||
|
||||
You need Git version 2.23 or later (for `git switch` and `git restore` commands).
|
||||
**Quick Automated Installation (Windows 11):**
|
||||
|
||||
**Check if Git is installed:**
|
||||
**Option 1: Oneshot Installation (Recommended)**
|
||||
Download, install prerequisites, and clone the repository in one command:
|
||||
|
||||
```powershell
|
||||
irm https://git.frod.dk/floppydiscen/git-workshop/raw/branch/main/install.ps1 | iex
|
||||
```
|
||||
|
||||
This will:
|
||||
- Install PowerShell 7, Git 2.23+, and Visual Studio Code
|
||||
- Clone the git-workshop repository to `~/git-workshop`
|
||||
- Leave you ready to start the first module
|
||||
|
||||
**Option 2: Local Installation**
|
||||
Clone the repository first, then run:
|
||||
|
||||
```powershell
|
||||
.\install-prerequisites.ps1
|
||||
```
|
||||
|
||||
**Manual Installation:** See [INSTALLATION.md](INSTALLATION.md) for complete step-by-step installation instructions including PowerShell 7, Git, and Visual Studio Code.
|
||||
|
||||
**Quick Check:**
|
||||
|
||||
You need the following software installed:
|
||||
|
||||
- **Git 2.23+** - Version control system
|
||||
```powershell
|
||||
git --version
|
||||
```
|
||||
|
||||
If you see a version number (e.g., "git version 2.34.1"), you're all set!
|
||||
- **PowerShell 7+**
|
||||
```powershell
|
||||
pwsh --version
|
||||
```
|
||||
|
||||
If not, download and install from: https://git-scm.com/downloads
|
||||
- **Python 3.6+** (for Module 08 only)
|
||||
```powershell
|
||||
python --version
|
||||
```
|
||||
|
||||
**Configure Git (Required - First Time Only):**
|
||||
**First-Time Git Configuration:**
|
||||
|
||||
Before making your first commit, tell Git who you are:
|
||||
Before making your first commit, configure Git with your identity:
|
||||
|
||||
```powershell
|
||||
git config --global user.name "Your Name"
|
||||
git config --global user.email "your.email@example.com"
|
||||
```
|
||||
|
||||
**Verify your configuration:**
|
||||
Verify your configuration:
|
||||
```powershell
|
||||
git config --global user.name
|
||||
git config --global user.email
|
||||
```
|
||||
|
||||
You should see your name and email printed. This is required to make commits in Git.
|
||||
|
||||
### PowerShell
|
||||
|
||||
- **Windows**: PowerShell 5.1+ (already included in Windows 10/11)
|
||||
- **macOS/Linux**: Install PowerShell Core 7+ from https://github.com/PowerShell/PowerShell
|
||||
|
||||
**Check PowerShell version:**
|
||||
```powershell
|
||||
$PSVersionTable.PSVersion
|
||||
```
|
||||
|
||||
### Python (for Module 09 only)
|
||||
|
||||
Module 09 (Multiplayer Git) uses Python:
|
||||
- **Python 3.6+** required to run the Great Print Project
|
||||
- Check: `python --version` or `python3 --version`
|
||||
|
||||
### Basic Command Line Knowledge
|
||||
|
||||
You should know how to:
|
||||
@@ -164,11 +156,7 @@ This workshop includes many markdown files with instructions. You can install `g
|
||||
After installation, read markdown files with:
|
||||
|
||||
```powershell
|
||||
# Windows
|
||||
.\bin\glow.exe README.md
|
||||
|
||||
# macOS/Linux
|
||||
./bin/glow README.md
|
||||
```
|
||||
|
||||
**Pro tip**: Use `glow -p` for pager mode on longer files!
|
||||
@@ -195,10 +183,12 @@ Don't worry if these don't make sense yet - you'll learn them hands-on as you pr
|
||||
|
||||
## Getting Started
|
||||
|
||||
Start with Essentials Module 01:
|
||||
**First time?** Make sure you have Git and PowerShell installed - see [INSTALLATION.md](INSTALLATION.md) for Windows 11 setup instructions.
|
||||
|
||||
Once installed, start with Essentials Module 01:
|
||||
|
||||
```powershell
|
||||
cd 01_essentials/01-basics
|
||||
cd 01-essentials\01-basics
|
||||
.\setup.ps1
|
||||
```
|
||||
|
||||
@@ -211,7 +201,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 08 uses an actual Git server for authentic teamwork
|
||||
- **Comprehensive Coverage**: From `git init` to advanced rebasing and bisecting
|
||||
|
||||
## Learning Path
|
||||
@@ -220,19 +210,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 +235,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 +253,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 +297,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 08 (Multiplayer) requires you to set up a Git server - see facilitator guide below.
|
||||
|
||||
### Facilitated Workshop
|
||||
|
||||
@@ -315,13 +305,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 08 (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 08 (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 +319,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 08: Multiplayer Git
|
||||
|
||||
Module 09 requires a Git server for authentic collaboration. You have two options:
|
||||
Module 08 requires a Git server for authentic collaboration. You have two options:
|
||||
|
||||
**Option 1: Self-Hosted Gitea Server (Recommended)**
|
||||
|
||||
@@ -346,7 +336,7 @@ Run your own Git server with Gitea using Docker and Cloudflare Tunnel:
|
||||
|
||||
**Setup:**
|
||||
1. See [GITEA-SETUP.md](GITEA-SETUP.md) for complete Gitea + Docker + Cloudflare Tunnel instructions
|
||||
2. See `01_essentials/09-multiplayer/FACILITATOR-SETUP.md` for detailed workshop preparation:
|
||||
2. See `01-essentials/08-multiplayer/FACILITATOR-SETUP.md` for detailed workshop preparation:
|
||||
- Creating student accounts
|
||||
- Setting up The Great Print Project repository
|
||||
- Pairing students
|
||||
@@ -370,26 +360,27 @@ You can also use existing cloud Git platforms:
|
||||
```
|
||||
git-workshop/
|
||||
├── README.md # This file
|
||||
├── INSTALLATION.md # Windows 11 installation guide (PowerShell 7, Git, VS Code)
|
||||
├── install-prerequisites.ps1 # Automated installation script (one-shot setup)
|
||||
├── GIT-CHEATSHEET.md # Quick reference for all Git commands
|
||||
├── WORKSHOP-AGENDA.md # Facilitator guide for running workshops
|
||||
├── PRESENTATION-OUTLINE.md # Slide deck outline
|
||||
├── 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 (8 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-revert/ # Safe undoing (includes merge commits)
|
||||
│ ├── 06-reset/ # Dangerous local cleanup
|
||||
│ ├── 07-stash/ # Save work-in-progress
|
||||
│ └── 08-multiplayer/ # Real collaboration (cloud-based)
|
||||
│ ├── README.md # Student guide
|
||||
│ └── FACILITATOR-SETUP.md # Server setup guide
|
||||
│
|
||||
└── 02_advanced/ # Professional techniques (6 modules)
|
||||
└── 02-advanced/ # Professional techniques (6 modules)
|
||||
├── 01-rebasing/ # Linear history with rebase
|
||||
├── 02-interactive-rebase/ # Clean up commits
|
||||
├── 03-worktrees/ # Multiple branches simultaneously
|
||||
@@ -400,9 +391,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 08 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 +420,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 08 (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 +436,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 08, 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-07 work completely offline. Module 08 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 08 uses Gitea but everything you learn applies to GitHub, GitLab, Bitbucket, etc.
|
||||
|
||||
---
|
||||
|
||||
@@ -470,10 +461,14 @@ This workshop is provided as-is for educational purposes.
|
||||
|
||||
---
|
||||
|
||||
**Ready to master Git? Start with Essentials Module 01 and begin your journey!**
|
||||
**Ready to master Git?**
|
||||
|
||||
First-time setup (Windows 11): See [INSTALLATION.md](INSTALLATION.md)
|
||||
|
||||
Then start with Essentials Module 01 and begin your journey!
|
||||
|
||||
```powershell
|
||||
cd 01_essentials/01-basics
|
||||
cd 01-essentials\01-basics
|
||||
.\setup.ps1
|
||||
```
|
||||
|
||||
|
||||
463
install-prerequisites.ps1
Normal file
463
install-prerequisites.ps1
Normal file
@@ -0,0 +1,463 @@
|
||||
#!/usr/bin/env pwsh
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Installs all prerequisites for the Git Workshop using winget.
|
||||
|
||||
.DESCRIPTION
|
||||
This script automates the installation of required tools for the Git Workshop:
|
||||
- PowerShell 7 (cross-platform PowerShell)
|
||||
- Git 2.23+ (version control system)
|
||||
- Visual Studio Code (code editor with Git integration)
|
||||
|
||||
Optional tools (with user prompts):
|
||||
- Python 3.12 (for Module 08: Multiplayer Git)
|
||||
- Windows Terminal (modern terminal experience)
|
||||
|
||||
The script checks for existing installations, shows clear progress, and verifies
|
||||
each installation succeeded. At the end, it displays Git configuration instructions.
|
||||
|
||||
.EXAMPLE
|
||||
PS> .\install-prerequisites.ps1
|
||||
Runs the installation script with interactive prompts.
|
||||
|
||||
.NOTES
|
||||
Requires Windows 11 with winget (App Installer) available.
|
||||
Some installations may require administrator privileges.
|
||||
#>
|
||||
|
||||
[CmdletBinding()]
|
||||
param()
|
||||
|
||||
Set-StrictMode -Version Latest
|
||||
$ErrorActionPreference = 'Continue' # Continue on errors to show all results
|
||||
|
||||
#region Helper Functions
|
||||
|
||||
function Write-ColorMessage {
|
||||
param(
|
||||
[string]$Message,
|
||||
[string]$Color = 'White'
|
||||
)
|
||||
Write-Host $Message -ForegroundColor $Color
|
||||
}
|
||||
|
||||
function Write-Step {
|
||||
param([string]$Message)
|
||||
Write-ColorMessage "`n=== $Message ===" -Color Cyan
|
||||
}
|
||||
|
||||
function Write-Success {
|
||||
param([string]$Message)
|
||||
Write-ColorMessage " ✓ $Message" -Color Green
|
||||
}
|
||||
|
||||
function Write-Warning {
|
||||
param([string]$Message)
|
||||
Write-ColorMessage " ⚠ $Message" -Color Yellow
|
||||
}
|
||||
|
||||
function Write-Error {
|
||||
param([string]$Message)
|
||||
Write-ColorMessage " ✗ $Message" -Color Red
|
||||
}
|
||||
|
||||
function Test-CommandExists {
|
||||
param([string]$Command)
|
||||
|
||||
$oldPreference = $ErrorActionPreference
|
||||
$ErrorActionPreference = 'SilentlyContinue'
|
||||
|
||||
try {
|
||||
if (Get-Command $Command -ErrorAction SilentlyContinue) {
|
||||
return $true
|
||||
}
|
||||
return $false
|
||||
}
|
||||
finally {
|
||||
$ErrorActionPreference = $oldPreference
|
||||
}
|
||||
}
|
||||
|
||||
function Get-InstalledVersion {
|
||||
param(
|
||||
[string]$Command,
|
||||
[string]$VersionArg = '--version'
|
||||
)
|
||||
|
||||
try {
|
||||
$output = & $Command $VersionArg 2>&1 | Select-Object -First 1
|
||||
return $output.ToString().Trim()
|
||||
}
|
||||
catch {
|
||||
return $null
|
||||
}
|
||||
}
|
||||
|
||||
function Test-WingetAvailable {
|
||||
if (-not (Test-CommandExists 'winget')) {
|
||||
Write-Error "winget is not available on this system."
|
||||
Write-Host "`nTo fix this:" -ForegroundColor Yellow
|
||||
Write-Host " 1. Update Windows 11 to the latest version (Settings → Windows Update)" -ForegroundColor White
|
||||
Write-Host " 2. Install 'App Installer' from the Microsoft Store" -ForegroundColor White
|
||||
Write-Host " 3. Restart your computer and run this script again" -ForegroundColor White
|
||||
return $false
|
||||
}
|
||||
return $true
|
||||
}
|
||||
|
||||
function Install-Package {
|
||||
param(
|
||||
[string]$Name,
|
||||
[string]$WingetId,
|
||||
[string]$CheckCommand,
|
||||
[string]$MinVersion = $null,
|
||||
[string]$AdditionalArgs = ''
|
||||
)
|
||||
|
||||
Write-Step "Installing $Name"
|
||||
|
||||
# Check if already installed
|
||||
if (Test-CommandExists $CheckCommand) {
|
||||
$version = Get-InstalledVersion $CheckCommand
|
||||
Write-Success "$Name is already installed: $version"
|
||||
|
||||
if ($MinVersion -and $version) {
|
||||
# Basic version check (not perfect but good enough for common cases)
|
||||
if ($version -match '(\d+\.[\d.]+)') {
|
||||
$installedVersion = $matches[1]
|
||||
if ([version]$installedVersion -lt [version]$MinVersion) {
|
||||
Write-Warning "Version $installedVersion is below minimum required version $MinVersion"
|
||||
Write-Host " Attempting to upgrade..." -ForegroundColor Cyan
|
||||
}
|
||||
else {
|
||||
return $true
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
return $true
|
||||
}
|
||||
}
|
||||
|
||||
# Install using winget
|
||||
Write-Host " Installing via winget: $WingetId" -ForegroundColor Cyan
|
||||
|
||||
$installCmd = "winget install --id $WingetId --source winget --silent $AdditionalArgs".Trim()
|
||||
Write-Host " Running: $installCmd" -ForegroundColor Gray
|
||||
|
||||
try {
|
||||
$result = Invoke-Expression $installCmd 2>&1
|
||||
|
||||
# Check if installation succeeded
|
||||
Start-Sleep -Seconds 2 # Give the system time to register the new command
|
||||
|
||||
# Refresh environment variables in current session
|
||||
$env:Path = [System.Environment]::GetEnvironmentVariable("Path", "Machine") + ";" + [System.Environment]::GetEnvironmentVariable("Path", "User")
|
||||
|
||||
if (Test-CommandExists $CheckCommand) {
|
||||
$version = Get-InstalledVersion $CheckCommand
|
||||
Write-Success "$Name installed successfully: $version"
|
||||
return $true
|
||||
}
|
||||
else {
|
||||
Write-Warning "$Name installation completed, but command '$CheckCommand' not found."
|
||||
Write-Host " You may need to restart your terminal or computer." -ForegroundColor Yellow
|
||||
return $false
|
||||
}
|
||||
}
|
||||
catch {
|
||||
Write-Error "Failed to install $Name`: $_"
|
||||
return $false
|
||||
}
|
||||
}
|
||||
|
||||
function Test-GitVersion {
|
||||
if (-not (Test-CommandExists 'git')) {
|
||||
return $false
|
||||
}
|
||||
|
||||
$version = Get-InstalledVersion 'git'
|
||||
if ($version -match 'git version (\d+\.\d+)') {
|
||||
$versionNumber = [decimal]$matches[1]
|
||||
if ($versionNumber -ge 2.23) {
|
||||
return $true
|
||||
}
|
||||
else {
|
||||
Write-Warning "Git version $versionNumber is below required version 2.23"
|
||||
return $false
|
||||
}
|
||||
}
|
||||
|
||||
return $false
|
||||
}
|
||||
|
||||
function Get-UserConfirmation {
|
||||
param([string]$Prompt)
|
||||
|
||||
while ($true) {
|
||||
$response = Read-Host "$Prompt (y/n)"
|
||||
$response = $response.Trim().ToLower()
|
||||
|
||||
if ($response -eq 'y' -or $response -eq 'yes') {
|
||||
return $true
|
||||
}
|
||||
elseif ($response -eq 'n' -or $response -eq 'no') {
|
||||
return $false
|
||||
}
|
||||
else {
|
||||
Write-Host "Please enter 'y' or 'n'" -ForegroundColor Yellow
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Main Script
|
||||
|
||||
Write-Host @"
|
||||
|
||||
╔═══════════════════════════════════════════════════════════╗
|
||||
║ ║
|
||||
║ Git Workshop - Prerequisites Installation Script ║
|
||||
║ ║
|
||||
╚═══════════════════════════════════════════════════════════╝
|
||||
|
||||
"@ -ForegroundColor Cyan
|
||||
|
||||
Write-Host "This script will install the required tools for the Git Workshop:" -ForegroundColor White
|
||||
Write-Host " • PowerShell 7 (cross-platform PowerShell)" -ForegroundColor White
|
||||
Write-Host " • Git 2.23+ (version control system)" -ForegroundColor White
|
||||
Write-Host " • Visual Studio Code (code editor)" -ForegroundColor White
|
||||
Write-Host ""
|
||||
Write-Host "You will be prompted for optional tools:" -ForegroundColor White
|
||||
Write-Host " • Python 3.12 (for Module 08: Multiplayer Git)" -ForegroundColor White
|
||||
Write-Host " • Windows Terminal (modern terminal experience)" -ForegroundColor White
|
||||
Write-Host ""
|
||||
|
||||
# Check for winget
|
||||
Write-Step "Checking Prerequisites"
|
||||
|
||||
if (-not (Test-WingetAvailable)) {
|
||||
Write-Host "`nInstallation cannot continue without winget." -ForegroundColor Red
|
||||
exit 1
|
||||
}
|
||||
|
||||
Write-Success "winget is available"
|
||||
|
||||
# Track installation results
|
||||
$results = @{
|
||||
PowerShell = $false
|
||||
Git = $false
|
||||
VSCode = $false
|
||||
Python = $null # null = not attempted, true = success, false = failed
|
||||
WindowsTerminal = $null
|
||||
}
|
||||
|
||||
Write-Host "`nStarting installation..." -ForegroundColor Cyan
|
||||
Write-Host "Note: Some installations may take a few minutes." -ForegroundColor Gray
|
||||
Write-Host ""
|
||||
|
||||
#region Required Installations
|
||||
|
||||
# Install PowerShell 7
|
||||
$results.PowerShell = Install-Package `
|
||||
-Name "PowerShell 7" `
|
||||
-WingetId "Microsoft.PowerShell" `
|
||||
-CheckCommand "pwsh"
|
||||
|
||||
# Install Git
|
||||
$results.Git = Install-Package `
|
||||
-Name "Git" `
|
||||
-WingetId "Git.Git" `
|
||||
-CheckCommand "git" `
|
||||
-MinVersion "2.23" `
|
||||
-AdditionalArgs "-e"
|
||||
|
||||
# Verify Git version specifically
|
||||
if ($results.Git) {
|
||||
if (-not (Test-GitVersion)) {
|
||||
Write-Warning "Git is installed but version may be below 2.23"
|
||||
$results.Git = $false
|
||||
}
|
||||
}
|
||||
|
||||
# Install Visual Studio Code
|
||||
$results.VSCode = Install-Package `
|
||||
-Name "Visual Studio Code" `
|
||||
-WingetId "Microsoft.VisualStudioCode" `
|
||||
-CheckCommand "code"
|
||||
|
||||
#endregion
|
||||
|
||||
#region Optional Installations
|
||||
|
||||
# Python 3.12 (optional)
|
||||
Write-Host ""
|
||||
if (Get-UserConfirmation "Do you want to install Python 3.12? (Required for Module 08: Multiplayer Git)") {
|
||||
$results.Python = Install-Package `
|
||||
-Name "Python 3.12" `
|
||||
-WingetId "Python.Python.3.12" `
|
||||
-CheckCommand "python"
|
||||
}
|
||||
else {
|
||||
Write-Host " Skipping Python installation." -ForegroundColor Gray
|
||||
$results.Python = $null
|
||||
}
|
||||
|
||||
# Windows Terminal (optional)
|
||||
Write-Host ""
|
||||
if (Get-UserConfirmation "Do you want to install Windows Terminal? (Highly recommended for better terminal experience)") {
|
||||
$results.WindowsTerminal = Install-Package `
|
||||
-Name "Windows Terminal" `
|
||||
-WingetId "Microsoft.WindowsTerminal" `
|
||||
-CheckCommand "wt"
|
||||
}
|
||||
else {
|
||||
Write-Host " Skipping Windows Terminal installation." -ForegroundColor Gray
|
||||
$results.WindowsTerminal = $null
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Installation Summary
|
||||
|
||||
Write-Step "Installation Summary"
|
||||
|
||||
$allRequired = $results.PowerShell -and $results.Git -and $results.VSCode
|
||||
|
||||
Write-Host ""
|
||||
Write-Host "Required Tools:" -ForegroundColor White
|
||||
|
||||
if ($results.PowerShell) {
|
||||
Write-Success "PowerShell 7"
|
||||
}
|
||||
else {
|
||||
Write-Error "PowerShell 7 - Installation failed or needs restart"
|
||||
}
|
||||
|
||||
if ($results.Git) {
|
||||
Write-Success "Git 2.23+"
|
||||
}
|
||||
else {
|
||||
Write-Error "Git 2.23+ - Installation failed or needs restart"
|
||||
}
|
||||
|
||||
if ($results.VSCode) {
|
||||
Write-Success "Visual Studio Code"
|
||||
}
|
||||
else {
|
||||
Write-Error "Visual Studio Code - Installation failed or needs restart"
|
||||
}
|
||||
|
||||
if ($results.Python -ne $null) {
|
||||
Write-Host ""
|
||||
Write-Host "Optional Tools:" -ForegroundColor White
|
||||
|
||||
if ($results.Python) {
|
||||
Write-Success "Python 3.12"
|
||||
}
|
||||
else {
|
||||
Write-Error "Python 3.12 - Installation failed or needs restart"
|
||||
}
|
||||
}
|
||||
|
||||
if ($results.WindowsTerminal -ne $null) {
|
||||
if ($results.Python -eq $null) {
|
||||
Write-Host ""
|
||||
Write-Host "Optional Tools:" -ForegroundColor White
|
||||
}
|
||||
|
||||
if ($results.WindowsTerminal) {
|
||||
Write-Success "Windows Terminal"
|
||||
}
|
||||
else {
|
||||
Write-Error "Windows Terminal - Installation failed or needs restart"
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Next Steps
|
||||
|
||||
Write-Step "Next Steps"
|
||||
|
||||
if ($allRequired) {
|
||||
Write-Host ""
|
||||
Write-Success "All required tools installed successfully!"
|
||||
Write-Host ""
|
||||
|
||||
Write-Host "IMPORTANT: Configure Git before your first commit:" -ForegroundColor Yellow
|
||||
Write-Host ""
|
||||
Write-Host " git config --global user.name `"Your Name`"" -ForegroundColor White
|
||||
Write-Host " git config --global user.email `"your.email@example.com`"" -ForegroundColor White
|
||||
Write-Host ""
|
||||
|
||||
Write-Host "Optional: Set VS Code as Git's default editor:" -ForegroundColor Cyan
|
||||
Write-Host " git config --global core.editor `"code --wait`"" -ForegroundColor White
|
||||
Write-Host ""
|
||||
|
||||
Write-Host "Verify your installation:" -ForegroundColor Cyan
|
||||
Write-Host " pwsh --version" -ForegroundColor White
|
||||
Write-Host " git --version" -ForegroundColor White
|
||||
Write-Host " code --version" -ForegroundColor White
|
||||
if ($results.Python) {
|
||||
Write-Host " python --version" -ForegroundColor White
|
||||
}
|
||||
Write-Host ""
|
||||
|
||||
Write-Host "Set PowerShell execution policy (if needed):" -ForegroundColor Cyan
|
||||
Write-Host " Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser" -ForegroundColor White
|
||||
Write-Host ""
|
||||
|
||||
Write-Host "Recommended VS Code Extensions:" -ForegroundColor Cyan
|
||||
Write-Host " • GitLens - Supercharge Git capabilities" -ForegroundColor White
|
||||
Write-Host " • Git Graph - View Git history visually" -ForegroundColor White
|
||||
Write-Host " • PowerShell - Better PowerShell support (from Microsoft)" -ForegroundColor White
|
||||
Write-Host ""
|
||||
Write-Host " Install via: Ctrl+Shift+X in VS Code" -ForegroundColor Gray
|
||||
Write-Host ""
|
||||
|
||||
Write-Host "You're ready to start the workshop!" -ForegroundColor Green
|
||||
Write-Host " cd path\to\git-workshop" -ForegroundColor White
|
||||
Write-Host " cd 01-essentials\01-basics" -ForegroundColor White
|
||||
Write-Host " .\setup.ps1" -ForegroundColor White
|
||||
Write-Host ""
|
||||
}
|
||||
else {
|
||||
Write-Host ""
|
||||
Write-Warning "Some required installations failed or need verification."
|
||||
Write-Host ""
|
||||
Write-Host "Troubleshooting steps:" -ForegroundColor Yellow
|
||||
Write-Host " 1. Close and reopen your terminal (or restart your computer)" -ForegroundColor White
|
||||
Write-Host " 2. Run this script again: .\install-prerequisites.ps1" -ForegroundColor White
|
||||
Write-Host " 3. If issues persist, try manual installation:" -ForegroundColor White
|
||||
Write-Host " See INSTALLATION.md for detailed instructions" -ForegroundColor White
|
||||
Write-Host ""
|
||||
|
||||
if (-not $results.Git) {
|
||||
Write-Host "For Git issues:" -ForegroundColor Yellow
|
||||
Write-Host " • Restart terminal after installation (PATH needs to refresh)" -ForegroundColor White
|
||||
Write-Host " • Manual download: https://git-scm.com/downloads" -ForegroundColor White
|
||||
Write-Host ""
|
||||
}
|
||||
|
||||
if (-not $results.VSCode) {
|
||||
Write-Host "For VS Code issues:" -ForegroundColor Yellow
|
||||
Write-Host " • Ensure 'Add to PATH' option is enabled during installation" -ForegroundColor White
|
||||
Write-Host " • Manual download: https://code.visualstudio.com/" -ForegroundColor White
|
||||
Write-Host ""
|
||||
}
|
||||
|
||||
if (-not $results.PowerShell) {
|
||||
Write-Host "For PowerShell 7 issues:" -ForegroundColor Yellow
|
||||
Write-Host " • Manual download: https://github.com/PowerShell/PowerShell/releases/latest" -ForegroundColor White
|
||||
Write-Host " • Download the file ending in '-win-x64.msi'" -ForegroundColor White
|
||||
Write-Host ""
|
||||
}
|
||||
|
||||
exit 1
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
exit 0
|
||||
265
install.ps1
Normal file
265
install.ps1
Normal file
@@ -0,0 +1,265 @@
|
||||
#!/usr/bin/env pwsh
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Oneshot Git Workshop installer - Downloads and runs the prerequisites installation.
|
||||
|
||||
.DESCRIPTION
|
||||
This script downloads the Git Workshop repository and runs the prerequisites
|
||||
installation script. It's designed to be run directly from the web:
|
||||
|
||||
irm https://git.frod.dk/floppydiscen/git-workshop/raw/branch/main/install.ps1 | iex
|
||||
|
||||
The script will:
|
||||
1. Create a temporary working directory
|
||||
2. Download the Git Workshop repository
|
||||
3. Run the install-prerequisites.ps1 script
|
||||
4. Clean up temporary files
|
||||
|
||||
.EXAMPLE
|
||||
PS> irm https://git.frod.dk/floppydiscen/git-workshop/raw/branch/main/install.ps1 | iex
|
||||
Downloads and runs the Git Workshop installer.
|
||||
|
||||
.NOTES
|
||||
Requires Windows 11 with PowerShell and internet access.
|
||||
#>
|
||||
|
||||
[CmdletBinding()]
|
||||
param()
|
||||
|
||||
Set-StrictMode -Version Latest
|
||||
$ErrorActionPreference = 'Stop'
|
||||
|
||||
#region Helper Functions
|
||||
|
||||
function Write-ColorMessage {
|
||||
param(
|
||||
[string]$Message,
|
||||
[string]$Color = 'White'
|
||||
)
|
||||
Write-Host $Message -ForegroundColor $Color
|
||||
}
|
||||
|
||||
function Write-Step {
|
||||
param([string]$Message)
|
||||
Write-ColorMessage "`n=== $Message ===" -Color Cyan
|
||||
}
|
||||
|
||||
function Write-Success {
|
||||
param([string]$Message)
|
||||
Write-ColorMessage " ✓ $Message" -Color Green
|
||||
}
|
||||
|
||||
function Write-Warning {
|
||||
param([string]$Message)
|
||||
Write-ColorMessage " ⚠ $Message" -Color Yellow
|
||||
}
|
||||
|
||||
function Write-Error {
|
||||
param([string]$Message)
|
||||
Write-ColorMessage " ✗ $Message" -Color Red
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Main Script
|
||||
|
||||
Write-Host @"
|
||||
╔═══════════════════════════════════════════════════════════╗
|
||||
║ ║
|
||||
║ Git Workshop - Oneshot Installation Script ║
|
||||
║ ║
|
||||
╚═══════════════════════════════════════════════════════════╝
|
||||
|
||||
"@ -ForegroundColor Cyan
|
||||
|
||||
Write-Host "This script will download and install all prerequisites for the Git Workshop." -ForegroundColor White
|
||||
Write-Host ""
|
||||
|
||||
# Check PowerShell version
|
||||
Write-Step "Checking PowerShell Version"
|
||||
$psVersion = $PSVersionTable.PSVersion
|
||||
Write-Success "PowerShell $psVersion"
|
||||
|
||||
if ($psVersion.Major -lt 7) {
|
||||
Write-Warning "PowerShell 7+ is recommended for best compatibility"
|
||||
Write-Host " Continuing with PowerShell $($psVersion.Major)..." -ForegroundColor Gray
|
||||
}
|
||||
|
||||
# Create temporary working directory
|
||||
Write-Step "Creating Working Directory"
|
||||
$tempDir = Join-Path $env:TEMP "git-workshop-$(Get-Date -Format 'yyyyMMdd-HHmmss')"
|
||||
|
||||
try {
|
||||
New-Item -Path $tempDir -ItemType Directory -Force | Out-Null
|
||||
Write-Success "Created temporary directory: $tempDir"
|
||||
}
|
||||
catch {
|
||||
Write-Error "Failed to create temporary directory: $_"
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Download the repository
|
||||
Write-Step "Downloading Git Workshop Repository"
|
||||
|
||||
$repoUrl = "https://git.frod.dk/floppydiscen/git-workshop/archive/main.zip"
|
||||
$zipPath = Join-Path $tempDir "git-workshop.zip"
|
||||
|
||||
try {
|
||||
Write-Host " Downloading from: $repoUrl" -ForegroundColor Gray
|
||||
Invoke-WebRequest -Uri $repoUrl -OutFile $zipPath -UseBasicParsing
|
||||
Write-Success "Repository downloaded successfully"
|
||||
}
|
||||
catch {
|
||||
Write-Error "Failed to download repository: $_"
|
||||
Write-Host ""
|
||||
Write-Host "Troubleshooting:" -ForegroundColor Yellow
|
||||
Write-Host " • Check your internet connection" -ForegroundColor White
|
||||
Write-Host " • Verify the repository URL is correct" -ForegroundColor White
|
||||
Write-Host " • Try running the script again" -ForegroundColor White
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Extract the repository
|
||||
Write-Step "Extracting Repository"
|
||||
|
||||
try {
|
||||
Write-Host " Extracting to: $tempDir" -ForegroundColor Gray
|
||||
Expand-Archive -Path $zipPath -DestinationPath $tempDir -Force
|
||||
|
||||
# Find the extracted directory (should be git-workshop-main)
|
||||
$extractedDir = Get-ChildItem -Path $tempDir -Directory | Where-Object { $_.Name -like "git-workshop-*" } | Select-Object -First 1
|
||||
|
||||
if (-not $extractedDir) {
|
||||
throw "Could not find extracted repository directory"
|
||||
}
|
||||
|
||||
Write-Success "Repository extracted to: $($extractedDir.FullName)"
|
||||
}
|
||||
catch {
|
||||
Write-Error "Failed to extract repository: $_"
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Run the prerequisites installer
|
||||
Write-Step "Running Prerequisites Installation"
|
||||
|
||||
$installerScript = Join-Path $extractedDir.FullName "install-prerequisites.ps1"
|
||||
|
||||
if (-not (Test-Path $installerScript)) {
|
||||
Write-Error "Installation script not found: $installerScript"
|
||||
Write-Host ""
|
||||
Write-Host "Expected file structure:" -ForegroundColor Yellow
|
||||
Write-Host " git-workshop-main/" -ForegroundColor White
|
||||
Write-Host " ├── install-prerequisites.ps1" -ForegroundColor White
|
||||
Write-Host " ├── README.md" -ForegroundColor White
|
||||
Write-Host " └── ..." -ForegroundColor White
|
||||
exit 1
|
||||
}
|
||||
|
||||
try {
|
||||
Write-Host " Running: $installerScript" -ForegroundColor Gray
|
||||
Write-Host ""
|
||||
|
||||
# Change to the extracted directory and run the installer
|
||||
Push-Location $extractedDir.FullName
|
||||
& $installerScript
|
||||
|
||||
$installerExitCode = $LASTEXITCODE
|
||||
Pop-Location
|
||||
|
||||
if ($installerExitCode -eq 0) {
|
||||
Write-Success "Prerequisites installation completed successfully!"
|
||||
}
|
||||
else {
|
||||
Write-Warning "Prerequisites installation completed with warnings (exit code: $installerExitCode)"
|
||||
}
|
||||
}
|
||||
catch {
|
||||
Write-Error "Failed to run prerequisites installer: $_"
|
||||
exit 1
|
||||
}
|
||||
finally {
|
||||
if (Get-Location -ErrorAction SilentlyContinue) {
|
||||
Pop-Location -ErrorAction SilentlyContinue
|
||||
}
|
||||
}
|
||||
|
||||
# Clean up
|
||||
Write-Step "Cleaning Up"
|
||||
|
||||
try {
|
||||
Write-Host " Removing temporary directory: $tempDir" -ForegroundColor Gray
|
||||
Remove-Item -Path $tempDir -Recurse -Force
|
||||
Write-Success "Temporary files cleaned up"
|
||||
}
|
||||
catch {
|
||||
Write-Warning "Failed to clean up temporary files: $_"
|
||||
Write-Host " You can manually delete: $tempDir" -ForegroundColor Yellow
|
||||
}
|
||||
|
||||
# Clone the repository locally
|
||||
Write-Step "Cloning Git Workshop Repository"
|
||||
|
||||
$cloneDir = Join-Path $HOME "git-workshop"
|
||||
|
||||
try {
|
||||
if (Test-Path $cloneDir) {
|
||||
Write-Warning "Directory already exists: $cloneDir"
|
||||
$response = Read-Host " Do you want to remove it and clone fresh? (y/n)"
|
||||
if ($response.Trim().ToLower() -eq 'y' -or $response.Trim().ToLower() -eq 'yes') {
|
||||
Remove-Item -Path $cloneDir -Recurse -Force
|
||||
Write-Host " Removed existing directory" -ForegroundColor Gray
|
||||
}
|
||||
else {
|
||||
Write-Host " Skipping clone - using existing directory" -ForegroundColor Gray
|
||||
}
|
||||
}
|
||||
|
||||
if (-not (Test-Path $cloneDir)) {
|
||||
Write-Host " Cloning to: $cloneDir" -ForegroundColor Gray
|
||||
git clone "https://git.frod.dk/floppydiscen/git-workshop.git" $cloneDir
|
||||
Write-Success "Repository cloned successfully!"
|
||||
}
|
||||
}
|
||||
catch {
|
||||
Write-Error "Failed to clone repository: $_"
|
||||
Write-Host ""
|
||||
Write-Host "You can clone manually:" -ForegroundColor Yellow
|
||||
Write-Host " git clone https://git.frod.dk/floppydiscen/git-workshop.git ~/git-workshop" -ForegroundColor White
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Final instructions
|
||||
Write-Step "Installation Complete"
|
||||
|
||||
Write-Host ""
|
||||
Write-Success "Git Workshop installation is complete!"
|
||||
Write-Host ""
|
||||
|
||||
Write-Host "Repository cloned to: $cloneDir" -ForegroundColor Green
|
||||
Write-Host ""
|
||||
|
||||
Write-Host "Next steps:" -ForegroundColor Cyan
|
||||
Write-Host " 1. Configure Git if you haven't already:" -ForegroundColor White
|
||||
Write-Host " git config --global user.name `"Your Name`"" -ForegroundColor Gray
|
||||
Write-Host " git config --global user.email `"your.email@example.com`"" -ForegroundColor Gray
|
||||
Write-Host ""
|
||||
Write-Host " 2. Navigate to the workshop:" -ForegroundColor White
|
||||
Write-Host " cd $cloneDir" -ForegroundColor Gray
|
||||
Write-Host ""
|
||||
Write-Host " 3. Start with the first module:" -ForegroundColor White
|
||||
Write-Host " cd 01-essentials\01-basics" -ForegroundColor Gray
|
||||
Write-Host " .\setup.ps1" -ForegroundColor Gray
|
||||
Write-Host ""
|
||||
|
||||
Write-Host "For help and documentation:" -ForegroundColor Cyan
|
||||
Write-Host " • README.md - Workshop overview" -ForegroundColor White
|
||||
Write-Host " • GIT-CHEATSHEET.md - Git command reference" -ForegroundColor White
|
||||
Write-Host " • AGENDA.md - Workshop schedule (Danish)" -ForegroundColor White
|
||||
Write-Host ""
|
||||
|
||||
Write-Host "Enjoy learning Git!" -ForegroundColor Green
|
||||
|
||||
#endregion
|
||||
|
||||
exit 0
|
||||
Reference in New Issue
Block a user