16 Commits

Author SHA1 Message Date
Bjarke Sporring
c20041efde fix: stash tasks in readme 2026-01-21 12:59:32 +01:00
Bjarke Sporring
84ed357feb fix: pwsh doesn't like @{} since it's a pwsh hashtable. Should wrap in a string 2026-01-21 12:58:25 +01:00
Bjarke Sporring
9659d82d2a feat: add a FAQ section 2026-01-21 12:27:09 +01:00
Bjarke Sporring
dbba2da5f2 tweak(cherry-pick): be more explicit with usage of ls 2026-01-21 12:12:59 +01:00
Bjarke Sporring
1d0003bb70 refactor(cherry-pick): more ls commands to check content of challenge when switching branches 2026-01-21 12:08:13 +01:00
Bjarke Sporring
3a4fe8ce9e tweak: expand upon some explanations 2026-01-21 11:56:56 +01:00
Bjarke Sporring
13dd2317f9 cleanup: pathing issues 2026-01-21 11:56:31 +01:00
Bjarke Sporring
cac03b69e3 fix: cleanup debug Write-Hosts 2026-01-21 11:41:10 +01:00
Bjarke Sporring
19cf66e23d fix: consistent Git naming and explanations for some commands 2026-01-21 11:40:44 +01:00
Bjarke Sporring
a6b33a441e feat: add a "remember to save" hint 2026-01-21 11:40:22 +01:00
Bjarke Sporring
690b83cead fix: remove fallbacks for the Get-MainBranch check 2026-01-21 11:40:00 +01:00
Bjarke Sporring
c69b463f84 fix: proper check when verifying a merge 2026-01-21 11:39:30 +01:00
Bjarke Sporring
39d8ace75c fix: consistent naming of files 2026-01-21 10:53:35 +01:00
Bjarke Sporring
4b9d2449c8 refactor: use the new utils.ps1 script for operations 2026-01-21 10:51:59 +01:00
Bjarke Sporring
5582b9fcbd refactor: reorder intro files 2026-01-21 10:51:34 +01:00
Bjarke Sporring
3a6eb0646b feat: add util.ps1 script for common operations 2026-01-21 10:51:07 +01:00
22 changed files with 340 additions and 438 deletions

View File

@@ -11,35 +11,35 @@
- Committed both required files (welcome.txt and instructions.txt)
#>
. "$PSScriptRoot\..\..\util.ps1"
Write-Host "Verifying Module 01: Git Basics Challenge..." -ForegroundColor Cyan
Write-Host ""
$allChecksPassed = $true
$challengeRoot = "$PSScriptRoot\challenge"
# Check if challenge directory exists
if (-not (Test-Path "challenge")) {
Write-Host "[FAIL] Challenge directory not found. Run setup.ps1 first." -ForegroundColor Red
if (-not (Test-Path $challengeRoot)) {
Write-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] No git repository found. Did you run 'git init'?" -ForegroundColor Red
Set-Location ".."
if (-not (Test-Path "$challengeRoot\.git")) {
Write-Fail "No git repository found. Did you run 'git init'?" -ForegroundColor Red
exit 1
}
Write-Host "[PASS] Git repository initialized" -ForegroundColor Green
Write-Pass "Git repository initialized" -ForegroundColor Green
# Check if there are any commits
$commitCount = (git rev-list --all --count 2>$null)
if ($null -eq $commitCount -or $commitCount -eq 0) {
Write-Host "[FAIL] No commits found. Have you committed your changes?" -ForegroundColor Red
Write-Fail "No commits found. Have you committed your changes?" -ForegroundColor Red
$allChecksPassed = $false
} else {
Write-Host "[PASS] Found $commitCount commit(s)" -ForegroundColor Green
Write-Pass "Found $commitCount commit(s)" -ForegroundColor Green
}
# Check if both files are in the git history using git ls-tree
@@ -47,16 +47,16 @@ if ($commitCount -gt 0) {
$trackedFiles = git ls-tree -r HEAD --name-only 2>$null
if ($trackedFiles -match "welcome.txt") {
Write-Host "[PASS] welcome.txt is committed" -ForegroundColor Green
Write-Pass "welcome.txt is committed" -ForegroundColor Green
} else {
Write-Host "[FAIL] welcome.txt is not in the commit history" -ForegroundColor Red
Write-Fail "welcome.txt is not in the commit history" -ForegroundColor Red
$allChecksPassed = $false
}
if ($trackedFiles -match "instructions.txt") {
Write-Host "[PASS] instructions.txt is committed" -ForegroundColor Green
Write-Pass "instructions.txt is committed" -ForegroundColor Green
} else {
Write-Host "[FAIL] instructions.txt is not in the commit history" -ForegroundColor Red
Write-Fail "instructions.txt is not in the commit history" -ForegroundColor Red
$allChecksPassed = $false
}
}
@@ -67,8 +67,6 @@ if ($statusOutput) {
Write-Host "[INFO] You have uncommitted changes. This is OK, but make sure all required files are committed." -ForegroundColor Yellow
}
Set-Location ".."
Write-Host ""
if ($allChecksPassed) {
Write-Host "========================================" -ForegroundColor Green

View File

@@ -44,6 +44,7 @@ The setup script will create an `answers.md` file in the challenge directory wit
7. View specific commits: `git show <commit-hash>`
8. Compare specific commits: `git diff <commit1> <commit2> <file>`
9. Fill in your answers in `answers.md`
10. Remember to save `answers.md`
> **Important Notes:**
> - You can use any Git commands you like to explore the repository

View File

@@ -10,52 +10,53 @@
- answers.txt exists with correct information about commit history
#>
. "$PSScriptRoot\..\..\util.ps1"
Write-Host "`n=== Verifying Module 02 Solution ===" -ForegroundColor Cyan
$allChecksPassed = $true
$challengeRoot = "$PSScriptRoot\challenge"
# Check if challenge directory exists
if (-not (Test-Path "challenge")) {
Write-Host "[FAIL] Challenge directory not found. Did you run setup.ps1?" -ForegroundColor Red
if (-not (Test-Path $challengeRoot)) {
Write-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 ..
if (-not (Test-Path "$challengeRoot\.git")) {
Write-Fail "Not a git repository. Did you run setup.ps1?" -ForegroundColor Red
exit 1
}
# Check if answers.md exists
if (-not (Test-Path "answers.md")) {
Write-Host "[FAIL] answers.md not found. Did you run setup.ps1?" -ForegroundColor Red
Write-Host "[HINT] The setup script should have created answers.md for you" -ForegroundColor Yellow
if (-not (Test-Path "$challengeRoot\answers.md")) {
Write-Fail "answers.md not found. Did you run setup.ps1?" -ForegroundColor Red
Write-Hint "The setup script should have created answers.md for you" -ForegroundColor Yellow
$allChecksPassed = $false
} else {
Write-Host "[PASS] answers.md exists" -ForegroundColor Green
Write-Pass "answers.md exists" -ForegroundColor Green
# Read the answers file
$answers = Get-Content "answers.md" -Raw
$answers = Get-Content "$challengeRoot\answers.md" -Raw
$answersLower = $answers.ToLower()
# Check 1: Contains "5" or "five" for commit count
if ($answersLower -match "5|five|fem") {
Write-Host "[PASS] Correct commit count found" -ForegroundColor Green
Write-Pass "Correct commit count found" -ForegroundColor Green
} else {
Write-Host "[FAIL] Commit count not found or incorrect" -ForegroundColor Red
Write-Host "[HINT] Use 'git log --oneline' to count commits easily" -ForegroundColor Yellow
Write-Fail "Commit count not found or incorrect" -ForegroundColor Red
Write-Hint "Use 'git log --oneline' to count commits easily" -ForegroundColor Yellow
$allChecksPassed = $false
}
# Check 2: Contains "database" keyword for third commit
if ($answersLower -match "database") {
Write-Host "[PASS] Third commit message identified" -ForegroundColor Green
Write-Pass "Third commit message identified" -ForegroundColor Green
} else {
Write-Host "[FAIL] Third commit message not found" -ForegroundColor Red
Write-Host "[HINT] Use 'git log' to see commit messages in order" -ForegroundColor Yellow
Write-Fail "Third commit message not found" -ForegroundColor Red
Write-Hint "Use 'git log' to see commit messages in order" -ForegroundColor Yellow
$allChecksPassed = $false
}
@@ -64,38 +65,37 @@ if (-not (Test-Path "answers.md")) {
$hasDatabase = $answersLower -match "database"
if ($hasAuth -and $hasDatabase) {
Write-Host "[PASS] Both files identified for bug fix commit" -ForegroundColor Green
Write-Pass "Both files identified for bug fix commit" -ForegroundColor Green
} elseif ($hasAuth -or $hasDatabase) {
Write-Host "[PARTIAL] Only one file found - there are TWO files modified in this commit" -ForegroundColor Yellow
Write-Host "[HINT] Use 'git log --stat' or 'git show <commit-hash> --name-only' to see ALL files changed" -ForegroundColor Yellow
Write-Hint "Use 'git log --stat' or 'git show <commit-hash> --name-only' to see ALL files changed" -ForegroundColor Yellow
$allChecksPassed = $false
} else {
Write-Host "[FAIL] Files modified in bug fix commit not found" -ForegroundColor Red
Write-Host "[HINT] Use 'git log --stat' to see which files were changed in each commit" -ForegroundColor Yellow
Write-Fail "Files modified in bug fix commit not found" -ForegroundColor Red
Write-Hint "Use 'git log --stat' to see which files were changed in each commit" -ForegroundColor Yellow
$allChecksPassed = $false
}
# Check 4: Contains "config" keyword for staged file
if ($answersLower -match "config.py") {
Write-Host "[PASS] Staged file identified" -ForegroundColor Green
Write-Pass "Staged file identified" -ForegroundColor Green
} else {
Write-Host "[FAIL] Staged file not identified" -ForegroundColor Red
Write-Host "[HINT] Use 'git status' or 'git diff --staged' to see staged changes" -ForegroundColor Yellow
Write-Fail "Staged file not identified" -ForegroundColor Red
Write-Hint "Use 'git status' or 'git diff --staged' to see staged changes" -ForegroundColor Yellow
$allChecksPassed = $false
}
# Check 5: Contains "unicorn" for the secret code
if ($answersLower -match "unicorn") {
Write-Host "[PASS] Secret code found!" -ForegroundColor Green
Write-Pass "Secret code found!" -ForegroundColor Green
} else {
Write-Host "[FAIL] Secret code not found" -ForegroundColor Red
Write-Host "[HINT] Use 'git diff <commit3> <commit4> database.py' to find the secret code" -ForegroundColor Yellow
Write-Fail "Secret code not found" -ForegroundColor Red
Write-Hint "Use 'git diff <commit3> <commit4> database.py' to find the secret code" -ForegroundColor Yellow
$allChecksPassed = $false
}
}
Set-Location ..
# Final summary
if ($allChecksPassed) {

View File

@@ -94,7 +94,8 @@ Now practice creating your own branch:
2. Check the changes you committed before. You'll notice that they're gone!
3. Now edit a file or create a new file (perhaps GUIDE.md, content of the file doesn't matter) and add it and commit it on your `main` branch (hint: `git add .`, `git commit -m`)
- This way we create diverging branches. The `main` branch has changes as well as your new `my-feature` branch.
- Run `git log --oneline --graph --all` to see how the tree is looking
- To see changes on the branch you're on, just run `git log --oneline --graph`
- Run `git log --oneline --graph --all` to see how the tree is looking, `main` should have diverged
4. Switch back to your branch `git switch my-feature`
5. The changes from the `main` branch are now gone. Check the changes you committed before. You'll notice they're back!
@@ -104,12 +105,14 @@ Bring your work into main:
1. Go back to the main branch `git switch main`
2. Run a `git log --oneline --graph --all` to see that the `HEAD` is on your `main` branch
3. It might be wise to first ensure that we're using Visual Studio Code to handle merge messages. If you haven't already set `git config --global core.editor "code --wait"` this sets Visual Studio Code to be the default editor for anything Git related.
- Hint: your `HEAD` is git's way of telling you <q>you're here</q>
3. It might be wise to first ensure that we're using Visual Studio Code to handle merge messages. If you haven't already set `git config --global core.editor "code --wait"` this sets Visual Studio Code to be the default editor for anything git related
4. Let's merge the recently created branch `git merge my-feature`
5. Visual Studio Code should open with a commit message. In order to solidify the commit simply close the window. That tells Git that the commit message has been written and the change should be committed.
5. Visual Studio Code should open with a commit message. In order to solidify the commit simply close the window. That tells git that the commit message has been written and the change should be committed
6. Now run `git log --oneline --graph --all` and see your changes merge into the `main` branch!
7. Now let's clean up a bit, run `git branch -d my-feature` to remove the recently merged branch.
- If you hadn't merged the branch first this command would fail as Git will warn you that you have changes not merged into the `main` branch
7. Now let's clean up a bit, run `git branch -d my-feature` to remove the recently merged branch
- If you hadn't merged the branch first this command would fail as git will warn you that you have changes not merged into the `main` branch Remember, once we've deleted the branch, we can no longer access it, and it's lost forever. That's why there is a safeguard when changes are unmerged when running
- Technically it's possible to recover the branch, however this requires using `git reflog` within the git garbage collection period which is usually 30 days-ish. This is however advanced, but I'd recommend asking your local LLM a question like <q>How can I restore a deleted branch</q>. It will probably come up with the right answer
You should see your feature branch merged into main!
@@ -139,7 +142,7 @@ git merge another-feature
### What is a Branch?
A **branch** is a lightweight movable pointer to a commit. When you create a branch, Git creates a new pointer - it doesn't copy all your files!
A **branch** is a lightweight movable pointer to a commit. When you create a branch, git creates a new pointer - it doesn't copy all your files!
```
main: A---B---C
@@ -154,7 +157,7 @@ my-feature: D---E
### What is HEAD?
`HEAD` points to your current branch. It's Git's way of saying "you are here."
`HEAD` points to your current branch. It's git's way of saying "you are here."
```pwsh
# HEAD points to main
@@ -181,7 +184,7 @@ main: A---B---C---M
feature: D---E
```
Git creates a merge commit `M` that has two parents (C and E).
git creates a merge commit `M` that has two parents (C and E).
**Fast-forward merge**:
```
@@ -194,7 +197,7 @@ After merge:
main: A---B---C---D
```
If main hasn't changed, Git just moves the pointer forward. No merge commit needed!
If main hasn't changed, git just moves the pointer forward. No merge commit needed!
## Key Commands
@@ -340,7 +343,7 @@ After completing this module, you understand:
## Next Steps
Ready to continue? The next module covers **merge conflicts** - what happens when Git can't automatically merge changes.
Ready to continue? The next module covers **merge conflicts** - what happens when git can't automatically merge changes.
To start over:
```pwsh
@@ -348,4 +351,4 @@ To start over:
.\setup.ps1
```
**Need help?** Review the commands above, or run `git status` to see what Git suggests!
**Need help?** Review the commands above, or run `git status` to see what git suggests!

View File

@@ -12,47 +12,20 @@
$script:allChecksPassed = $true
# ============================================================================
# Helper Functions
# ============================================================================
function Write-Pass {
param([string]$Message)
Write-Host "[PASS] $Message" -ForegroundColor Green
}
function Write-Fail {
param([string]$Message)
Write-Host "[FAIL] $Message" -ForegroundColor Red
$script:allChecksPassed = $false
}
function Write-Hint {
param([string]$Message)
Write-Host "[HINT] $Message" -ForegroundColor Yellow
}
function Write-Info {
param([string]$Message)
Write-Host "[INFO] $Message" -ForegroundColor Cyan
}
$challengeRoot = "$PSScriptRoot\challenge"
# ============================================================================
# Check challenge directory exists
# ============================================================================
if (-not (Test-Path "challenge")) {
if (-not (Test-Path $challengeRoot)) {
Write-Host "[ERROR] Challenge directory not found." -ForegroundColor Red
Write-Host "Run .\setup.ps1 first to create the challenge environment." -ForegroundColor Yellow
exit 1
}
Push-Location "challenge"
if (-not (Test-Path ".git")) {
if (-not (Test-Path "$challengeRoot\.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
}
@@ -62,24 +35,7 @@ Write-Host "`n=== Verifying Module 03: Branching and Merging ===" -ForegroundCol
# Detect the main branch name (could be main, master, etc.)
# ============================================================================
# Try to get the default branch from remote origin first
$mainBranch = git symbolic-ref refs/remotes/origin/HEAD 2>$null | Split-Path -Leaf
if (-not $mainBranch) {
# Fallback: try to detect from local branches
$allBranches = git branch --list 2>$null | ForEach-Object { $_.Trim('* ') }
if ($allBranches -contains "main") {
$mainBranch = "main"
} elseif ($allBranches -contains "master") {
$mainBranch = "master"
} else {
# Get the default branch from git config
$mainBranch = git config --get init.defaultBranch
if (-not $mainBranch) {
# Ultimate fallback: use the first branch
$mainBranch = $allBranches | Select-Object -First 1
if (-not $mainBranch) { $mainBranch = "main" }
}
}
}
$mainBranch = Get-MainBranch
Write-Host "Detected main branch: $mainBranch" -ForegroundColor Cyan
# ============================================================================

View File

@@ -22,16 +22,16 @@ This creates a repository with two feature branches that have conflicting change
## Overview
A **merge conflict** occurs when Git cannot automatically combine changes because both branches modified the same part of the same file in different ways.
A **merge conflict** occurs when git cannot automatically combine changes because both branches modified the same part of the same file in different ways.
**When do conflicts happen?**
- ✅ Two branches modify the same lines in a file
- ✅ One branch deletes a file that another branch modifies
- ✅ Complex changes Git can't merge automatically
- ✅ 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.
**Don't fear conflicts!** They're a normal part of collaborative development. git just needs your help to decide what the final code should look like.
## Your Task
@@ -249,7 +249,7 @@ For this challenge, we want **both settings**, so:
### Part 9: Mark the Conflict as Resolved
Tell Git you've resolved the conflict:
Tell git you've resolved the conflict:
```bash
# Stage the resolved file
@@ -267,7 +267,7 @@ All conflicts fixed but you are still merging.
(use "git commit" to conclude merge)
```
Perfect! Git confirms the conflict is resolved.
Perfect! git confirms the conflict is resolved.
### Part 10: Complete the Merge
@@ -277,7 +277,7 @@ Commit the merge:
git commit
```
Git will open an editor with a default merge message. You can accept it or customize it, then save and close.
git will open an editor with a default merge message. You can accept it or customize it, then save and close.
**Done!** Your merge is complete!
@@ -436,7 +436,7 @@ git diff main..feature-branch
## Merge Tools
Git supports visual merge tools that make resolving conflicts easier:
git supports visual merge tools that make resolving conflicts easier:
```bash
# Configure a merge tool (one-time setup)
@@ -484,4 +484,4 @@ To start over:
.\setup.ps1
```
**Need help?** Review the steps above, or run `git status` to see what Git suggests!
**Need help?** Review the steps above, or run `git status` to see what git suggests!

View File

@@ -11,49 +11,26 @@
- Ensuring valid JSON syntax
#>
. "$PSScriptRoot\..\..\util.ps1"
$script:allChecksPassed = $true
$challengeRoot = "$PSScriptRoot\challenge"
# ============================================================================
# Helper Functions
# ============================================================================
function Write-Pass {
param([string]$Message)
Write-Host "[PASS] $Message" -ForegroundColor Green
}
function Write-Fail {
param([string]$Message)
Write-Host "[FAIL] $Message" -ForegroundColor Red
$script:allChecksPassed = $false
}
function Write-Hint {
param([string]$Message)
Write-Host "[HINT] $Message" -ForegroundColor Yellow
}
function Write-Info {
param([string]$Message)
Write-Host "[INFO] $Message" -ForegroundColor Cyan
}
Write-Host $PSScriptRoot
# ============================================================================
# Check challenge directory exists
# ============================================================================
if (-not (Test-Path "challenge")) {
Write-Host "[ERROR] Challenge directory not found." -ForegroundColor Red
if (-not (Test-Path $challengeRoot)) {
Write-Error "Challenge directory not found." -ForegroundColor Red
Write-Host "Run .\setup.ps1 first to create the challenge environment." -ForegroundColor Yellow
exit 1
}
Push-Location "challenge"
if (-not (Test-Path ".git")) {
Write-Host "[ERROR] Not a git repository." -ForegroundColor Red
Write-Host "Run ..\setup.ps1 first to create the challenge environment." -ForegroundColor Yellow
Pop-Location
if (-not (Test-Path "$challengeRoot\.git")) {
Write-Error "Not a git repository." -ForegroundColor Red
Write-Host "Run .\setup.ps1 first to create the challenge environment." -ForegroundColor Yellow
exit 1
}
@@ -63,24 +40,7 @@ Write-Host "`n=== Verifying Module 04: Merge Conflicts ===" -ForegroundColor Cya
# Detect the main branch name (could be main, master, etc.)
# ============================================================================
# Try to get the default branch from remote origin first
$mainBranch = git symbolic-ref refs/remotes/origin/HEAD 2>$null | Split-Path -Leaf
if (-not $mainBranch) {
# Fallback: try to detect from local branches
$allBranches = git branch --list 2>$null | ForEach-Object { $_.Trim('* ') }
if ($allBranches -contains "main") {
$mainBranch = "main"
} elseif ($allBranches -contains "master") {
$mainBranch = "master"
} else {
# Get the default branch from git config
$mainBranch = git config --get init.defaultBranch
if (-not $mainBranch) {
# Ultimate fallback: use the first branch
$mainBranch = $allBranches | Select-Object -First 1
if (-not $mainBranch) { $mainBranch = "main" }
}
}
}
$mainBranch = Get-MainBranch
Write-Host "Detected main branch: $mainBranch" -ForegroundColor Cyan
# ============================================================================
@@ -97,22 +57,20 @@ if ($currentBranch -eq $mainBranch) {
# ============================================================================
# Check that merge is not in progress
# ============================================================================
if (Test-Path ".git/MERGE_HEAD") {
if (Test-Path "$challengeRoot\.git\MERGE_HEAD") {
Write-Fail "Merge is still in progress (conflicts not resolved)"
Write-Hint "Resolve conflicts in config.json, then: git add config.json && git commit"
Pop-Location
exit 1
} else {
Write-Pass "No merge in progress (conflicts resolved)"
}
# ============================================================================
# Check if config.json exists
# ============================================================================
if (-not (Test-Path "config.json")) {
if (-not (Test-Path "$challengeRoot\config.json")) {
Write-Fail "File 'config.json' not found"
Write-Hint "The config.json file should exist"
Pop-Location
exit 1
}
@@ -120,14 +78,13 @@ if (-not (Test-Path "config.json")) {
# Verify config.json is valid JSON
# ============================================================================
try {
$configContent = Get-Content "config.json" -Raw
$configContent = Get-Content "$challengeRoot\config.json" -Raw
$config = $configContent | ConvertFrom-Json -ErrorAction Stop
Write-Pass "File 'config.json' is valid JSON"
} catch {
Write-Fail "File 'config.json' is not valid JSON"
Write-Hint "Make sure you removed all conflict markers (<<<<<<<, =======, >>>>>>>)"
Write-Hint "Check for missing commas or brackets"
Pop-Location
exit 1
}
@@ -137,7 +94,6 @@ try {
if ($configContent -match '<<<<<<<|=======|>>>>>>>') {
Write-Fail "Conflict markers still present in config.json"
Write-Hint "Remove all conflict markers (<<<<<<<, =======, >>>>>>>)"
Pop-Location
exit 1
} else {
Write-Pass "No conflict markers in config.json"
@@ -163,8 +119,11 @@ if ($config.app.debug -eq $true) {
# ============================================================================
# Verify both branches were merged
# ============================================================================
$addTimeoutMerged = git log --oneline --grep="add-timeout" 2>$null | Select-String "Merge"
$addDebugMerged = git log --oneline --grep="add-debug" 2>$null | Select-String "Merge"
git merge-base --is-ancestor add-timeout main
$addTimeoutMerged = $LASTEXITCODE -eq 0
git merge-base --is-ancestor add-debug main
$addDebugMerged = $LASTEXITCODE -eq 0
if ($addTimeoutMerged) {
Write-Pass "add-timeout branch has been merged"
@@ -197,7 +156,6 @@ if ($totalCommits -ge 5) {
Write-Hint "Make sure both branches are merged"
}
Pop-Location
# ============================================================================
# Final summary

View File

@@ -52,10 +52,13 @@ First, see what commits are on the development branch:
```pwsh
cd challenge
# View all commits on development branch
git log --oneline development
# First let's see what files are in the current branch and notice that there is a file security.py
ls
# View the full commit graph
# View all commits on development branch
git log --oneline --graph
# View the full commit graph for all branches
git log --oneline --graph --all
```
@@ -87,6 +90,9 @@ git switch main
# Verify you're on main
git branch
# See what files exist. Notice that we no longer have a security.py file
ls
```
The `*` should be next to `main`.
@@ -96,8 +102,6 @@ The `*` should be next to `main`.
# See main's commits
git log --oneline
# See what files exist
ls
```
Main should only have the initial app and README - no bug fixes yet, no experimental features.
@@ -113,6 +117,7 @@ Now copy the bug fix commits from development to main:
2. Cherry-pick the security fix:
```pwsh
git cherry-pick <security-fix-hash>
ls # we now have the security.py file!
# Example if the hash is abc1234:
# git cherry-pick abc1234
@@ -125,6 +130,7 @@ Now copy the bug fix commits from development to main:
5. Cherry-pick the performance fix:
```pwsh
git cherry-pick <performance-fix-hash>
ls # we now have the cache.py file!
```
6. Verify both fixes are now on main:

View File

@@ -10,6 +10,8 @@
the bug fixes to the main branch.
#>
. "$PSScriptRoot\..\..\util.ps1"
# Remove existing challenge directory if present
if (Test-Path "challenge") {
Write-Host "Removing existing challenge directory..." -ForegroundColor Yellow
@@ -42,11 +44,7 @@ git add app.py
git commit -m "Initial app implementation" | Out-Null
# Detect the main branch name after first commit
$mainBranch = git branch --show-current
if (-not $mainBranch) {
$mainBranch = git config --get init.defaultBranch
if (-not $mainBranch) { $mainBranch = "main" }
}
$mainBranch = Get-MainBranch
Write-Host "Default branch detected: $mainBranch" -ForegroundColor Yellow
$readme = @"

View File

@@ -9,168 +9,136 @@
to the main branch without merging the experimental features.
#>
Set-Location "challenge" -ErrorAction SilentlyContinue
. "$PSScriptRoot\..\..\util.ps1"
# 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
}
$challengeRoot = "$PSScriptRoot\challenge"
if (-not (Test-Path ".")) {
if (-not (Test-Path $challengeRoot)) {
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 ..
if (-not (Test-Path "$challengeRoot\.git")) {
Write-Fail "No git repository found." -ForegroundColor Red
exit 1
}
# Detect the main branch name
$allBranches = git branch --list 2>$null | ForEach-Object { $_.Trim('* ') }
if ($allBranches -contains "main") {
$mainBranch = "main"
} elseif ($allBranches -contains "master") {
$mainBranch = "master"
} else {
$mainBranch = git config --get init.defaultBranch
if (-not $mainBranch) {
$mainBranch = $allBranches | Select-Object -First 1
if (-not $mainBranch) { $mainBranch = "main" }
}
}
$mainBranch = Get-MainBranch
Write-Host "Detected main branch: $mainBranch" -ForegroundColor Cyan
# Check current branch
$currentBranch = git branch --show-current 2>$null
if ($currentBranch -ne $mainBranch) {
Write-Host "[FAIL] You should be on the '$mainBranch' branch." -ForegroundColor Red
Write-Fail "You should be on the '$mainBranch' branch." -ForegroundColor Red
Write-Host "Current branch: $currentBranch" -ForegroundColor Yellow
Write-Host "Hint: Use 'git checkout $mainBranch' to switch to $mainBranch branch" -ForegroundColor Yellow
Set-Location ..
Write-Hint "Use 'git checkout $mainBranch' to switch to $mainBranch branch" -ForegroundColor Yellow
exit 1
}
# Check if there's an ongoing cherry-pick
if (Test-Path ".git/CHERRY_PICK_HEAD") {
Write-Host "[FAIL] Cherry-pick is not complete. There may be unresolved conflicts." -ForegroundColor Red
Write-Host "Hint: Resolve any conflicts, then use:" -ForegroundColor Yellow
if (Test-Path "$challengeRoot\.git\CHERRY_PICK_HEAD") {
Write-Fail "Cherry-pick is not complete. There may be unresolved conflicts." -ForegroundColor Red
Write-Hint "Resolve any conflicts, then use:" -ForegroundColor Yellow
Write-Host " git add <file>" -ForegroundColor White
Write-Host " git cherry-pick --continue" -ForegroundColor White
Write-Host "Or abort with: git cherry-pick --abort" -ForegroundColor White
Set-Location ..
exit 1
}
# Check commit count on main (should be 4: 2 initial + 2 cherry-picked)
$mainCommitCount = (git rev-list --count $mainBranch 2>$null)
if ($mainCommitCount -ne 4) {
Write-Host "[FAIL] Expected 4 commits on $mainBranch branch, found $mainCommitCount" -ForegroundColor Red
Write-Fail "Expected 4 commits on $mainBranch branch, found $mainCommitCount" -ForegroundColor Red
if ($mainCommitCount -lt 4) {
Write-Host "Hint: You should cherry-pick 2 bug fix commits to $mainBranch" -ForegroundColor Yellow
Write-Hint "You should cherry-pick 2 bug fix commits to $mainBranch" -ForegroundColor Yellow
} else {
Write-Host "Hint: You should cherry-pick ONLY the 2 bug fix commits, not all commits" -ForegroundColor Yellow
Write-Hint "You should cherry-pick ONLY the 2 bug fix commits, not all commits" -ForegroundColor Yellow
}
Write-Host "`nExpected commits on ${mainBranch}:" -ForegroundColor Yellow
Write-Host " 1. Initial app implementation" -ForegroundColor White
Write-Host " 2. Add README" -ForegroundColor White
Write-Host " 3. Fix security vulnerability in input validation (cherry-picked)" -ForegroundColor White
Write-Host " 4. Fix performance issue with data caching (cherry-picked)" -ForegroundColor White
Set-Location ..
exit 1
}
# Check for merge commits (should be none - cherry-pick doesn't create merge commits)
$mergeCommits = git log --merges --oneline $mainBranch 2>$null
if ($mergeCommits) {
Write-Host "[FAIL] Found merge commits on $mainBranch. You should use cherry-pick, not merge." -ForegroundColor Red
Write-Host "Hint: Use 'git cherry-pick <commit-hash>' instead of 'git merge'" -ForegroundColor Yellow
Set-Location ..
Write-Fail "Found merge commits on $mainBranch. You should use cherry-pick, not merge." -ForegroundColor Red
Write-Hint "Use 'git cherry-pick <commit-hash>' instead of 'git merge'" -ForegroundColor Yellow
exit 1
}
# Check that security.py exists (from the security fix commit)
if (-not (Test-Path "security.py")) {
Write-Host "[FAIL] security.py not found on $mainBranch branch." -ForegroundColor Red
Write-Host "Hint: You need to cherry-pick the 'Fix security vulnerability' commit" -ForegroundColor Yellow
Set-Location ..
if (-not (Test-Path "$challengeRoot\security.py")) {
Write-Fail "security.py not found on $mainBranch branch." -ForegroundColor Red
Write-Hint "You need to cherry-pick the 'Fix security vulnerability' commit" -ForegroundColor Yellow
exit 1
}
# Check that security.py has the security fix
$securityContent = Get-Content "security.py" -Raw
$securityContent = Get-Content "$challengeRoot\security.py" -Raw
if ($securityContent -notmatch "sanitize_input") {
Write-Host "[FAIL] security.py is missing the sanitize_input function." -ForegroundColor Red
Set-Location ..
Write-Fail "security.py is missing the sanitize_input function." -ForegroundColor Red
exit 1
}
if ($securityContent -notmatch "validate_token") {
Write-Host "[FAIL] security.py is missing the validate_token function." -ForegroundColor Red
Set-Location ..
Write-Fail "security.py is missing the validate_token function." -ForegroundColor Red
exit 1
}
# Check that app.py exists
if (-not (Test-Path "app.py")) {
Write-Host "[FAIL] app.py not found." -ForegroundColor Red
Set-Location ..
if (-not (Test-Path "$challengeRoot\app.py")) {
Write-Fail "app.py not found." -ForegroundColor Red
exit 1
}
# Check that cache.py exists (from performance fix)
if (-not (Test-Path "cache.py")) {
Write-Host "[FAIL] cache.py not found on $mainBranch branch." -ForegroundColor Red
Write-Host "Hint: You need to cherry-pick the 'Fix performance issue' commit" -ForegroundColor Yellow
Set-Location ..
if (-not (Test-Path "$challengeRoot\cache.py")) {
Write-Fail "cache.py not found on $mainBranch branch." -ForegroundColor Red
Write-Hint "You need to cherry-pick the 'Fix performance issue' commit" -ForegroundColor Yellow
exit 1
}
# Check that cache.py has the DataCache class
$cacheContent = Get-Content "cache.py" -Raw
$cacheContent = Get-Content "$challengeRoot\cache.py" -Raw
if ($cacheContent -notmatch "DataCache") {
Write-Host "[FAIL] cache.py is missing the DataCache class." -ForegroundColor Red
Set-Location ..
Write-Fail "cache.py is missing the DataCache class." -ForegroundColor Red
exit 1
}
if ($cacheContent -notmatch "def get\(") {
Write-Host "[FAIL] cache.py is missing the get method." -ForegroundColor Red
Set-Location ..
Write-Fail "cache.py is missing the get method." -ForegroundColor Red
exit 1
}
# Check that app.py does NOT have experimental features
$appContent = Get-Content "app.py" -Raw
$appContent = Get-Content "$challengeRoot\app.py" -Raw
# Should NOT have experimental features
if ($appContent -match "experimental_mode") {
Write-Host "[FAIL] app.py contains experimental features (experimental_mode)." -ForegroundColor Red
Write-Host "Hint: You should cherry-pick ONLY the bug fixes, not experimental features" -ForegroundColor Yellow
Write-Fail "app.py contains experimental features (experimental_mode)." -ForegroundColor Red
Write-Hint "You should cherry-pick ONLY the bug fixes, not experimental features" -ForegroundColor Yellow
Write-Host " The experimental feature commits should stay on development branch only" -ForegroundColor Yellow
Set-Location ..
exit 1
}
if ($appContent -match "beta_features") {
Write-Host "[FAIL] app.py contains experimental features (beta_features)." -ForegroundColor Red
Write-Host "Hint: You should cherry-pick ONLY the bug fixes, not experimental features" -ForegroundColor Yellow
Set-Location ..
Write-Fail "app.py contains experimental features (beta_features)." -ForegroundColor Red
Write-Hint "You should cherry-pick ONLY the bug fixes, not experimental features" -ForegroundColor Yellow
exit 1
}
if ($appContent -match "enable_experimental_features") {
Write-Host "[FAIL] app.py contains experimental features (enable_experimental_features)." -ForegroundColor Red
Write-Host "Hint: You should cherry-pick ONLY the bug fixes, not experimental features" -ForegroundColor Yellow
Set-Location ..
Write-Fail "app.py contains experimental features (enable_experimental_features)." -ForegroundColor Red
Write-Hint "You should cherry-pick ONLY the bug fixes, not experimental features" -ForegroundColor Yellow
exit 1
}
@@ -191,25 +159,22 @@ foreach ($commit in $commitArray) {
}
if (-not $hasSecurityFix) {
Write-Host "[FAIL] Security fix commit not found on $mainBranch branch." -ForegroundColor Red
Write-Host "Hint: Cherry-pick the 'Fix security vulnerability' commit from development" -ForegroundColor Yellow
Set-Location ..
Write-Fail "Security fix commit not found on $mainBranch branch." -ForegroundColor Red
Write-Hint "Cherry-pick the 'Fix security vulnerability' commit from development" -ForegroundColor Yellow
exit 1
}
if (-not $hasPerformanceFix) {
Write-Host "[FAIL] Performance fix commit not found on $mainBranch branch." -ForegroundColor Red
Write-Host "Hint: Cherry-pick the 'Fix performance issue' commit from development" -ForegroundColor Yellow
Set-Location ..
Write-Fail "Performance fix commit not found on $mainBranch branch." -ForegroundColor Red
Write-Hint "Cherry-pick the 'Fix performance issue' commit from development" -ForegroundColor Yellow
exit 1
}
# Verify development branch still has all commits
$devCommitCount = (git rev-list --count development 2>$null)
if ($devCommitCount -ne 6) {
Write-Host "[FAIL] Development branch should still have 6 commits." -ForegroundColor Red
Write-Fail "Development branch should still have 6 commits." -ForegroundColor Red
Write-Host "Found: $devCommitCount commits" -ForegroundColor Yellow
Set-Location ..
exit 1
}
@@ -226,5 +191,4 @@ Write-Host "`nPerfect use of cherry-pick!" -ForegroundColor Green
Write-Host "You selectively applied critical fixes without merging unfinished features.`n" -ForegroundColor Green
Write-Host "Try 'git log --oneline --graph --all' to see both branches." -ForegroundColor Cyan
Set-Location ..
exit 0

View File

@@ -52,6 +52,9 @@ You're working on a calculator application. A developer added a `divide` functio
1. **Navigate to the challenge directory:**
```pwsh
cd challenge
# and check the contents. We should notice that divide.py exists, this will be reverted
ls
```
2. **Check which branch you're on** (you should be on `regular-revert`):

View File

@@ -9,23 +9,23 @@
- multi-revert: Multiple commits reverted
#>
. "$PSScriptRoot\..\..\util.ps1"
Write-Host "`n=== Verifying Module 06: Git Revert Solutions ===" -ForegroundColor Cyan
$allChecksPassed = $true
$originalDir = Get-Location
$challengeRoot = "$PSScriptRoot\challenge"
# Check if challenge directory exists
if (-not (Test-Path "challenge")) {
Write-Host "[FAIL] Challenge directory not found. Run setup.ps1 first." -ForegroundColor Red
if (-not (Test-Path "$challengeRoot")) {
Write-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
if (-not (Test-Path "$challengeRoot\.git")) {
Write-Fail "Not a git repository. Run setup.ps1 first." -ForegroundColor Red
exit 1
}
@@ -37,50 +37,50 @@ 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
Write-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
Write-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
Write-Fail "No revert commit found" -ForegroundColor Red
Write-Hint "Use: git revert <commit-hash>" -ForegroundColor Yellow
$allChecksPassed = $false
}
# Check that divide.py is removed (was reverted)
if (-not (Test-Path "divide.py")) {
Write-Host "[PASS] Broken divide.py successfully reverted (file removed)" -ForegroundColor Green
if (-not (Test-Path "$challengeRoot\divide.py")) {
Write-Pass "Broken divide.py successfully reverted (file removed)" -ForegroundColor Green
} else {
Write-Host "[FAIL] divide.py still exists (should be reverted)" -ForegroundColor Red
Write-Host "[HINT] The bad commit should be reverted, removing divide.py" -ForegroundColor Yellow
Write-Fail "divide.py still exists (should be reverted)" -ForegroundColor Red
Write-Hint "The bad commit should be reverted, removing divide.py" -ForegroundColor Yellow
$allChecksPassed = $false
}
# Check that calculator.py exists and has correct content
if (Test-Path "calculator.py") {
$calcContent = Get-Content "calculator.py" -Raw
if (Test-Path "$challengeRoot\calculator.py") {
$calcContent = Get-Content "$challengeRoot\calculator.py" -Raw
# 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
Write-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
Write-Fail "modulo function missing (should still exist)" -ForegroundColor Red
Write-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
Write-Pass "multiply function preserved (good commit before bad one)" -ForegroundColor Green
} else {
Write-Host "[FAIL] multiply function missing" -ForegroundColor Red
Write-Fail "multiply function missing" -ForegroundColor Red
$allChecksPassed = $false
}
} else {
Write-Host "[FAIL] calculator.py not found" -ForegroundColor Red
Write-Fail "calculator.py not found" -ForegroundColor Red
$allChecksPassed = $false
}
}
@@ -93,7 +93,7 @@ Write-Host "`n=== Scenario 2: 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
Write-Fail "multi-revert branch not found" -ForegroundColor Red
$allChecksPassed = $false
} else {
# Count revert commits
@@ -101,56 +101,54 @@ if ($LASTEXITCODE -ne 0) {
$revertCount = ($revertCommits | Measure-Object).Count
if ($revertCount -ge 2) {
Write-Host "[PASS] Found $revertCount revert commits (expected at least 2)" -ForegroundColor Green
Write-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
Write-Fail "Found only $revertCount revert commit(s), need at least 2" -ForegroundColor Red
Write-Hint "Revert both bad commits: git revert <commit1> <commit2>" -ForegroundColor Yellow
$allChecksPassed = $false
}
# Check that sqrt.py is removed (reverted)
if (-not (Test-Path "sqrt.py")) {
Write-Host "[PASS] Broken sqrt.py successfully reverted (file removed)" -ForegroundColor Green
if (-not (Test-Path "$challengeRoot\sqrt.py")) {
Write-Pass "Broken sqrt.py successfully reverted (file removed)" -ForegroundColor Green
} else {
Write-Host "[FAIL] sqrt.py still exists (should be reverted)" -ForegroundColor Red
Write-Fail "sqrt.py still exists (should be reverted)" -ForegroundColor Red
$allChecksPassed = $false
}
# Check that logarithm.py is removed (reverted)
if (-not (Test-Path "logarithm.py")) {
Write-Host "[PASS] Broken logarithm.py successfully reverted (file removed)" -ForegroundColor Green
if (-not (Test-Path "$challengeRoot\logarithm.py")) {
Write-Pass "Broken logarithm.py successfully reverted (file removed)" -ForegroundColor Green
} else {
Write-Host "[FAIL] logarithm.py still exists (should be reverted)" -ForegroundColor Red
Write-Fail "logarithm.py still exists (should be reverted)" -ForegroundColor Red
$allChecksPassed = $false
}
# Check calculator.py content
if (Test-Path "calculator.py") {
$calcContent = Get-Content "calculator.py" -Raw
if (Test-Path "$challengeRoot\calculator.py") {
$calcContent = Get-Content "$challengeRoot\calculator.py" -Raw
# Check that power function still exists (good commit before bad ones)
if ($calcContent -match "def power") {
Write-Host "[PASS] power function preserved" -ForegroundColor Green
Write-Pass "power function preserved" -ForegroundColor Green
} else {
Write-Host "[FAIL] power function missing (should still exist)" -ForegroundColor Red
Write-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
Write-Pass "absolute function preserved" -ForegroundColor Green
} else {
Write-Host "[FAIL] absolute function missing (should still exist)" -ForegroundColor Red
Write-Fail "absolute function missing (should still exist)" -ForegroundColor Red
$allChecksPassed = $false
}
} else {
Write-Host "[FAIL] calculator.py not found" -ForegroundColor Red
Write-Fail "calculator.py not found" -ForegroundColor Red
$allChecksPassed = $false
}
}
Set-Location $originalDir
# Final summary
Write-Host ""
if ($allChecksPassed) {

View File

@@ -95,7 +95,8 @@ Suddenly, your teammate reports a **critical security bug** in production! You n
2. **Check your current status:**
```pwsh
git status
git status # See which files are changed
git diff # See all the changes in a diffview
```
You should see modified `login.py` (uncommitted changes)
@@ -116,15 +117,12 @@ Suddenly, your teammate reports a **critical security bug** in production! You n
```
6. **Open app.py and find the bug:**
```pwsh
cat app.py
```
Look for the comment "# BUG: This allows unauthenticated access!"
7. **Fix the bug** by editing app.py:
- Remove the buggy comment line
- You can leave the implementation as-is or improve it
- The important thing is removing the comment that says "allows unauthenticated access"
- The important thing is removing the comment that says "BUG: This allows unauthenticated access"
8. **Commit the fix:**
```pwsh
@@ -143,10 +141,8 @@ Suddenly, your teammate reports a **critical security bug** in production! You n
```
This applies the stash and removes it from the stash stack
11. **Complete the TODOs in login.py:**
11. **Remove all the TODOs in login.py:**
- Open login.py in your editor
- Complete the login method (verify password and return session)
- Add a logout method
- Remove all TODO comments
12. **Commit your completed feature:**
@@ -194,10 +190,7 @@ git stash pop
git stash apply
# Apply a specific stash
git stash apply stash@{1}
# Apply a specific stash by number
git stash apply 1
git stash apply "stash@{1}"
```
### Managing Stashes
@@ -207,7 +200,7 @@ git stash apply 1
git stash drop
# Drop a specific stash
git stash drop stash@{1}
git stash drop "stash@{1}"
# Clear all stashes
git stash clear
@@ -293,10 +286,10 @@ git stash pop
git stash list
# Show summary of what changed
git stash show stash@{0}
git stash show "stash@{0}"
# Show full diff
git stash show -p stash@{0}
git stash show -p "stash@{0}"
```
### "Stash conflicts when I apply"

View File

@@ -9,48 +9,28 @@
and completed the feature on the feature branch.
#>
Set-Location "challenge" -ErrorAction SilentlyContinue
$root = $PSScriptRoot
# 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 ..
if (-not (Test-Path "$root/challenge")) {
Write-Error "Challenge directory not found. Run setup.ps1 first." -ForegroundColor Red
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 ..
if (-not (Test-Path "$root/challenge/.git")) {
Write-Fail "No git repository found." -ForegroundColor Red
exit 1
}
# Detect the main branch name
$allBranches = git branch --list 2>$null | ForEach-Object { $_.Trim('* ') }
if ($allBranches -contains "main") {
$mainBranch = "main"
} elseif ($allBranches -contains "master") {
$mainBranch = "master"
} else {
$mainBranch = git config --get init.defaultBranch
if (-not $mainBranch) {
$mainBranch = $allBranches | Select-Object -First 1
if (-not $mainBranch) { $mainBranch = "main" }
}
}
$mainBranch = Get-MainBranch
Write-Host "Detected main branch: $mainBranch" -ForegroundColor Cyan
# Check current branch
$currentBranch = git branch --show-current 2>$null
if ($currentBranch -ne "feature-login") {
Write-Host "[FAIL] You should be on the 'feature-login' branch." -ForegroundColor Red
Write-Fail "You should be on the 'feature-login' branch." -ForegroundColor Red
Write-Host "Current branch: $currentBranch" -ForegroundColor Yellow
Write-Host "Hint: Switch back to feature-login after completing the challenge" -ForegroundColor Yellow
Set-Location ..
@@ -60,8 +40,8 @@ if ($currentBranch -ne "feature-login") {
# Check for uncommitted changes on feature-login
$status = git status --porcelain 2>$null
if ($status) {
Write-Host "[FAIL] You have uncommitted changes on feature-login." -ForegroundColor Red
Write-Host "Hint: After restoring from stash, you should complete and commit the feature" -ForegroundColor Yellow
Write-Fail "You have uncommitted changes on feature-login." -ForegroundColor Red
Write-Hint "After restoring from stash, you should complete and commit the feature" -ForegroundColor Yellow
git status --short
Set-Location ..
exit 1
@@ -69,7 +49,7 @@ if ($status) {
# Verify main branch has the security fix
Write-Host "`nChecking $mainBranch branch for bug fix..." -ForegroundColor Cyan
git checkout $mainBranch 2>$null | Out-Null
git switch $mainBranch 2>$null | Out-Null
# Check for bug fix commit
$mainCommits = git log --pretty=format:"%s" $mainBranch 2>$null
@@ -82,17 +62,17 @@ foreach ($commit in $mainCommits) {
}
if (-not $hasSecurityFix) {
Write-Host "[FAIL] No security bug fix commit found on $mainBranch branch." -ForegroundColor Red
Write-Fail "No security bug fix commit found on $mainBranch branch." -ForegroundColor Red
Write-Host "Hint: After stashing, switch to $mainBranch and commit a bug fix" -ForegroundColor Yellow
git checkout feature-login 2>$null | Out-Null
git switch feature-login 2>$null | Out-Null
Set-Location ..
exit 1
}
# Check that app.py has been fixed
if (-not (Test-Path "app.py")) {
Write-Host "[FAIL] app.py not found on $mainBranch branch." -ForegroundColor Red
git checkout feature-login 2>$null | Out-Null
Write-Fail "app.py not found on $mainBranch branch." -ForegroundColor Red
git switch feature-login 2>$null | Out-Null
Set-Location ..
exit 1
}
@@ -102,9 +82,9 @@ $appContent = Get-Content "app.py" -Raw
# The bug was "return true" in authenticate - it should be fixed now
# We'll check that the buggy comment is gone or the implementation is improved
if ($appContent -match "allows unauthenticated access") {
Write-Host "[FAIL] The security bug comment still exists in app.py." -ForegroundColor Red
Write-Fail "The security bug comment still exists in app.py." -ForegroundColor Red
Write-Host "Hint: Remove the bug from app.py and commit the fix" -ForegroundColor Yellow
git checkout feature-login 2>$null | Out-Null
git switch feature-login 2>$null | Out-Null
Set-Location ..
exit 1
}
@@ -113,14 +93,14 @@ Write-Host "[PASS] Security bug fixed on main!" -ForegroundColor Green
# Switch back to feature-login
Write-Host "`nChecking feature-login branch..." -ForegroundColor Cyan
git checkout feature-login 2>$null | Out-Null
git switch feature-login 2>$null | Out-Null
# Check for completed feature commit
$featureCommits = git log --pretty=format:"%s" feature-login 2>$null
$commitCount = ($featureCommits -split "`n").Count
if ($commitCount -lt 3) {
Write-Host "[FAIL] Expected at least 3 commits on feature-login." -ForegroundColor Red
Write-Fail "Expected at least 3 commits on feature-login." -ForegroundColor Red
Write-Host "Hint: You should have the initial commits plus your completed feature commit" -ForegroundColor Yellow
Set-Location ..
exit 1
@@ -128,7 +108,7 @@ if ($commitCount -lt 3) {
# Check that login.py exists
if (-not (Test-Path "login.py")) {
Write-Host "[FAIL] login.py not found on feature-login branch." -ForegroundColor Red
Write-Fail "login.py not found on feature-login branch." -ForegroundColor Red
Set-Location ..
exit 1
}
@@ -137,22 +117,22 @@ $loginContent = Get-Content "login.py" -Raw
# Check that login method exists and is implemented
if ($loginContent -notmatch "def login") {
Write-Host "[FAIL] login.py should have a login method." -ForegroundColor Red
Write-Fail "login.py should have a login method." -ForegroundColor Red
Set-Location ..
exit 1
}
# Check that TODOs are completed (no TODO comments should remain)
if ($loginContent -match "TODO") {
Write-Host "[FAIL] login.py still contains TODO comments." -ForegroundColor Red
Write-Host "Hint: Complete all the TODOs in login.py before committing" -ForegroundColor Yellow
Write-Fail "login.py still contains TODO comments." -ForegroundColor Red
Write-Hint "Complete all the TODOs in login.py before committing" -ForegroundColor Yellow
Set-Location ..
exit 1
}
# Check that password verification is implemented
if ($loginContent -notmatch "password") {
Write-Host "[FAIL] login method should verify the password." -ForegroundColor Red
Write-Fail "login method should verify the password." -ForegroundColor Red
Set-Location ..
exit 1
}
@@ -160,7 +140,7 @@ if ($loginContent -notmatch "password") {
# Check that the feature has been committed (not just in working directory)
$lastCommit = git log -1 --pretty=format:"%s" 2>$null
if ($lastCommit -notmatch "login|feature|complete|implement") {
Write-Host "[FAIL] Your completed feature should be committed." -ForegroundColor Red
Write-Fail "Your completed feature should be committed." -ForegroundColor Red
Write-Host "Last commit: $lastCommit" -ForegroundColor Yellow
Write-Host "Hint: After popping the stash and completing the TODOs, commit the feature" -ForegroundColor Yellow
Set-Location ..

View File

@@ -56,7 +56,7 @@ This creates a new branch and switches to it.
```powershell
git add .
git commit -m "fix: move 7 to correct position"
git push feature-2
git push
```
Your branch is now on Azure DevOps.

View File

@@ -19,8 +19,52 @@ Master fundamental Git concepts and collaborative workflows:
- **Module 07: Stash** - Temporarily save work without committing
- **Module 08: Multiplayer Git** - **The Great Print Project** - Real cloud-based collaboration with teammates
## Requirements
### Prerequisites
Install these tools before starting:
**PowerShell 7+**
Download here <https://learn.microsoft.com/en-us/powershell/scripting/install/install-powershell-on-windows?view=powershell-7.5#msi>.
```pwsh
winget install Microsoft.PowerShell
```
**Git 2.23+**
Download here <https://git-scm.com/install/windows> or install using the command down below
```pwsh
winget install Git.Git
```
**Visual Studio Code**
Download here <https://code.visualstudio.com/download> or install using the command down below
```pwsh
winget install Microsoft.VisualStudioCode
```
You can also run the one-shot install script if the project is cloned locally. It will check if you have the prerequisites install and clone down the project if not already cloned. Then it will configure git to have sane default.
```pwsh
./install.ps1
```
or if you don't have the project locally run.
REMEMBER read through the script before running it. As a general practice, you shouldn't get comfortable doing this kind of execution, and so it's important to review executing remote scripts.
```pwsh
Invoke-RestMethod -Uri https://git.frod.dk/floppydiscen/git-workshop/raw/branch/main/install.ps1 | Invoke-Expression
```
## How to Use This Workshop
### For Local Modules (01-07)
1. Navigate to a module directory (e.g., `01-essentials/01-basics`)
@@ -28,6 +72,7 @@ Master fundamental Git concepts and collaborative workflows:
3. Run `./setup.ps1` to create the challenge environment
4. Complete the challenge using git commands
5. Run `./verify.ps1` to check if you've solved it correctly
6. If you mess up the challenge just run `.\reset.ps1`
6. Move to the next module
**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!
@@ -56,6 +101,12 @@ If you encounter an "execution policy" error when running scripts, open PowerShe
Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser
```
And to revert back to the default policy
```pwsh
Set-ExecutionPolicy -ExecutionPolicy Restricted -Scope CurrentUser
```
Then run scripts using:
```pwsh
.\setup.ps1
@@ -63,49 +114,6 @@ Then run scripts using:
.\reset.ps1
```
## Requirements
### Prerequisites
Install these tools before starting:
**PowerShell 7+**
```pwsh
winget install Microsoft.PowerShell
```
**Git 2.23+**
```pwsh
winget install Git.Git
```
**Visual Studio Code**
```pwsh
winget install Microsoft.VisualStudioCode
```
You can also run the one-shot install script if the project is cloned locally.
It will check if you have the prerequisites install and clone down the project
if not already cloned. Then it will configure git to have sane default.
```pwsh
./install.ps1
```
or if you don't have the project locally run.
REMEMBER read through the script before running it. As a general practice, you
shouldn't get comfortable doing this kind of execution, and so it's important
to review executing remote scripts.
```pwsh
Invoke-RestMethod -Uri https://git.frod.dk/floppydiscen/git-workshop/raw/branch/main/install.ps1 | Invoke-Expression
```
### Quick Start
1. **Install the prerequisites above**
2. **Clone this repository**
3. **Configure Git** (see below for recommended settings)
## Git Configuration
Before starting the workshop, configure Git with your identity and recommended settings:
@@ -269,31 +277,3 @@ By completing this workshop, you'll be able to:
- **Use SSH keys for secure Git authentication**
These are professional-level Git skills used daily by developers at tech companies.
---
## Repository Structure
```
git-workshop/
├── README.md # This file
├── INSTALLATION.md # Installation guide
├── install.ps1 # Automated installation script
├── GIT-CHEATSHEET.md # Quick reference for all Git commands
├── BEST-PRACTICES.md # Git best practices
├── COMMIT-MESSAGES.md # Guide to writing good commit messages
├── WHAT-IS-GIT.md # Introduction to Git concepts
└── 01-essentials/ # Core Git skills (8 modules)
├── 01-basics/ # Initialize, commit, status
├── 02-history/ # Log, diff, show
├── 03-branching-and-merging/ # Branches and merging
├── 04-merge-conflict/ # Resolve merge conflicts
├── 05-cherry-pick/ # Apply specific commits
├── 06-revert/ # Safe undoing
├── 07-stash/ # Save work-in-progress
└── 08-multiplayer/ # Real collaboration (cloud-based)
├── README.md # Student guide
├── 01_FACILITATOR.md # Facilitator setup guide
└── 02_AZURE-DEVOPS-SSH-SETUP.md # SSH authentication guide
```

View File

@@ -110,7 +110,7 @@ Nothing creates unnecessary conflicts like two people reformatting the same file
**Do this:**
- Agree on code formatting standards as a team
- Use automatic formatters (Prettier, Black, etc.)
- Use automatic formatters (Prettier, Black, clang-format, etc.)
- Configure your editor to format on save
- Run formatters before committing
@@ -122,16 +122,22 @@ Renaming a widely-used function or moving files around will conflict with almost
**Do this:**
- Announce refactors to the team before starting
- This is important to coordinate merges of features that are being worked before the great refactoring.
A large renaming of namespaces or moving of files becomes hard to deal with when you rely on the old file-structure before the great merge.
Instead you should *freeze* any further features until the great refactoring has been completed and then resume feature-building
- Do them quickly and push immediately
- Consider doing them when others aren't actively working
- Keep refactoring commits separate from feature work
This keeps your commits clean and doesn't *dirty* the actual work with actual feature work (which also makes it easy to cherry-pick while minimizing conflicts).
## The Golden Rules
1. **Sync frequently** - Pull before you start, pull before you push
2. **Commit small** - Many small commits beat one large commit
A way to think of it is to keep commit *atomic*. In other words try to make them self-contained units that are easy to `cherry-pick` at a later time.
3. **Talk to your team** - A quick message prevents hours of conflict resolution
4. **Stay focused** - One branch = one purpose
Communication is key. Even though alot is delegated to the process itself, it's still a good idea to keep communicating with the team regarding larger changes or features being built.
4. **Stay focused** - One branch = one purpose (and ususally one person)
5. **Push promptly** - Don't sit on finished work
## When Conflicts Do Happen
@@ -140,7 +146,7 @@ Even with best practices, conflicts will occur. When they do:
1. **Don't panic** - Conflicts are normal, not failures
2. **Read carefully** - Understand both sides before choosing
3. **Test after resolving** - Make sure the merged code actually works
3. **Test after resolving** - Make sure the merged code actually works. A usual, and rather basic test flow is build -> test -> run
4. **Ask if unsure** - If you don't understand the other person's code, ask them
Remember: merge conflicts are a communication problem as much as a technical one. The best tool for reducing conflicts is talking to your team.

9
06-FAQ.md Normal file
View File

@@ -0,0 +1,9 @@
# Questions
## Is it possible to recover lost branches or commits?
Yes it is, however this is dependent on whether or not git has done an internal garbage collection which there usually is a retention period of 30 days.
**Beware** this is advanced territory and is usually only relevant when you've messed up with a `git reset` or a deletion of a branch
To read more, you can read the documentation here <https://git-scm.com/docs/git-reflog>

49
util.ps1 Normal file
View File

@@ -0,0 +1,49 @@
<#
.SYNOPSIS
Utility functions for writing to stdout and for advanced git commands that are reused again and again
#>
function Write-Pass {
param([string]$Message)
Write-Host "[PASS] $Message" -ForegroundColor Green
}
function Write-Fail {
param([string]$Message)
Write-Host "[FAIL] $Message" -ForegroundColor Red
$script:allChecksPassed = $false
}
function Write-Hint {
param([string]$Message)
Write-Host "[HINT] $Message" -ForegroundColor Yellow
}
function Write-Info {
param([string]$Message)
Write-Host "[INFO] $Message" -ForegroundColor Cyan
}
function Write-Error {
param([string] $Message)
Write-Host "[ERROR] $Message" -ForegroundColor Red
}
function Get-LocalBranches {
return git for-each-ref --format='%(refname:short)' refs/heads
}
function Get-MainBranch {
$mainBranch = git branch --show-current 2>$null
$allBranches = Get-LocalBranches
if ($allBranches -contains "main") {
$mainBranch = "main"
} elseif ($allBranches -contains "master") {
$mainBranch = "master"
} else {
$mainBranch = git config --get init.defaultBranch
}
return $mainBranch
}