diff --git a/module-07-rebasing/README.md b/module-07-rebasing/README.md new file mode 100644 index 0000000..e173e03 --- /dev/null +++ b/module-07-rebasing/README.md @@ -0,0 +1,130 @@ +# Module 07: Rebasing + +## Learning Objectives + +By the end of this module, you will: +- Understand what rebasing is and how it works +- Know the difference between merge and rebase +- Perform a rebase to integrate changes from one branch to another +- Understand when to use rebase vs merge +- Know the golden rule of rebasing + +## Challenge Description + +You have a repository with a `main` branch and a `feature` branch. While you were working on the feature branch, new commits were added to `main`. You want to incorporate those changes into your feature branch while maintaining a clean, linear history. + +Your task is to: +1. Review the current commit history +2. Rebase the `feature` branch onto `main` +3. Verify that the history is now linear + +## Key Concepts + +### What is Rebasing? + +Rebasing is the process of moving or combining a sequence of commits to a new base commit. Instead of creating a merge commit like `git merge` does, rebasing rewrites the commit history by replaying your commits on top of another branch. + +### Rebase vs Merge + +**Merge:** +``` + A---B---C feature + / \ + D---E---F---G main +``` +Creates a merge commit (G) that ties the histories together. + +**Rebase:** +``` + A'--B'--C' feature + / + D---E---F main +``` +Replays commits A, B, C on top of F, creating new commits A', B', C' with the same changes but different commit hashes. + +### Benefits of Rebasing + +- **Cleaner history**: Linear history is easier to read and understand +- **Simpler log**: No merge commits cluttering the history +- **Easier bisecting**: Finding bugs with `git bisect` is simpler with linear history + +### The Golden Rule of Rebasing + +**Never rebase commits that have been pushed to a public/shared repository.** + +Why? Because rebasing rewrites history by creating new commits. If others have based work on the original commits, rebasing will cause serious problems for collaborators. + +**Safe to rebase:** +- Local commits not yet pushed +- Feature branches you're working on alone +- Cleaning up your work before creating a pull request + +**Never rebase:** +- Commits already pushed to a shared branch (like `main` or `develop`) +- Commits that others might have based work on + +## Useful Commands + +```bash +# Rebase current branch onto another branch +git rebase + +# View commit history as a graph +git log --oneline --graph --all + +# If conflicts occur during rebase: +# 1. Resolve conflicts in files +# 2. Stage the resolved files +git add +# 3. Continue the rebase +git rebase --continue + +# Abort a rebase if something goes wrong +git rebase --abort + +# Check which branch you're on +git branch + +# Switch to a branch +git checkout +``` + +## Verification + +Run the verification script to check your solution: + +```bash +.\verify.ps1 +``` + +The verification will check that: +- You're on the feature branch +- The rebase was completed successfully +- The history is linear (no merge commits) +- All commits from both branches are present + +## Tips + +- Always check your commit graph with `git log --oneline --graph --all` before and after rebasing +- If you encounter conflicts during rebase, resolve them just like merge conflicts +- Use `git rebase --abort` if you want to cancel the rebase and start over +- Rebasing rewrites history, so the commit hashes will change +- Only rebase local commits that haven't been shared with others + +## When to Use Rebase vs Merge + +**Use Rebase when:** +- You want a clean, linear history +- Working on a local feature branch that hasn't been shared +- Updating your feature branch with the latest changes from main +- You want to clean up commits before submitting a pull request + +**Use Merge when:** +- Working on a shared/public branch +- You want to preserve the complete history including when branches diverged +- You're merging a completed feature into main +- You want to be safe and avoid rewriting history + +## What You'll Learn + +Rebasing is a powerful tool for maintaining a clean project history. While merging is safer and preserves exact history, rebasing creates a more readable linear timeline. Understanding both techniques and knowing when to use each is essential for effective Git workflow management. diff --git a/module-07-rebasing/reset.ps1 b/module-07-rebasing/reset.ps1 new file mode 100644 index 0000000..e08550d --- /dev/null +++ b/module-07-rebasing/reset.ps1 @@ -0,0 +1,22 @@ +#!/usr/bin/env pwsh + +<# +.SYNOPSIS + Resets the rebasing 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" diff --git a/module-07-rebasing/setup.ps1 b/module-07-rebasing/setup.ps1 new file mode 100644 index 0000000..1372419 --- /dev/null +++ b/module-07-rebasing/setup.ps1 @@ -0,0 +1,123 @@ +#!/usr/bin/env pwsh + +<# +.SYNOPSIS + Sets up the rebasing challenge environment. + +.DESCRIPTION + Creates a Git repository with diverged branches to practice rebasing. + The feature branch needs to be rebased onto main. +#> + +# 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 file and commit +$readme = @" +# My Project + +A sample project for learning Git rebasing. +"@ + +Set-Content -Path "README.md" -Value $readme +git add README.md +git commit -m "Initial commit" | Out-Null + +# Create and switch to feature branch +git checkout -b feature | Out-Null + +# Add commits on feature branch +$feature1 = @" +# My Project + +A sample project for learning Git rebasing. + +## Features + +- Feature A: User authentication +"@ + +Set-Content -Path "README.md" -Value $feature1 +git add README.md +git commit -m "Add feature A" | Out-Null + +$feature2 = @" +# My Project + +A sample project for learning Git rebasing. + +## Features + +- Feature A: User authentication +- Feature B: Data validation +"@ + +Set-Content -Path "README.md" -Value $feature2 +git add README.md +git commit -m "Add feature B" | Out-Null + +# Switch back to main and add commits (simulating other work happening on main) +git checkout main | Out-Null + +$main1 = @" +# My Project + +A sample project for learning Git rebasing. + +## Installation + +Run \`npm install\` to install dependencies. +"@ + +Set-Content -Path "README.md" -Value $main1 +git add README.md +git commit -m "Add installation instructions" | Out-Null + +$main2 = @" +# My Project + +A sample project for learning Git rebasing. + +## Installation + +Run \`npm install\` to install dependencies. + +## Configuration + +Copy \`config.example.json\` to \`config.json\` and update settings. +"@ + +Set-Content -Path "README.md" -Value $main2 +git add README.md +git commit -m "Add configuration instructions" | Out-Null + +# Switch back to feature branch +git checkout 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 are now on the 'feature' branch." -ForegroundColor Cyan +Write-Host "The 'main' branch has new commits that aren't in your feature branch." -ForegroundColor Cyan +Write-Host "`nYour task:" -ForegroundColor Yellow +Write-Host "1. Navigate to the challenge directory: cd challenge" -ForegroundColor White +Write-Host "2. View the commit history: git log --oneline --graph --all" -ForegroundColor White +Write-Host "3. Rebase the 'feature' branch onto 'main'" -ForegroundColor White +Write-Host "4. View the history again to see the linear result" -ForegroundColor White +Write-Host "`nRun '../verify.ps1' from the challenge directory to check your solution.`n" -ForegroundColor Cyan diff --git a/module-07-rebasing/verify.ps1 b/module-07-rebasing/verify.ps1 new file mode 100644 index 0000000..f5434de --- /dev/null +++ b/module-07-rebasing/verify.ps1 @@ -0,0 +1,154 @@ +#!/usr/bin/env pwsh + +<# +.SYNOPSIS + Verifies the rebasing challenge solution. + +.DESCRIPTION + Checks that the user successfully rebased the feature branch onto main, + resulting in a clean, linear history. +#> + +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 "feature") { + Write-Host "[FAIL] You should be on the 'feature' branch." -ForegroundColor Red + Write-Host "Current branch: $currentBranch" -ForegroundColor Yellow + Write-Host "Hint: Use 'git checkout feature' to switch to feature branch" -ForegroundColor Yellow + Set-Location .. + exit 1 +} + +# Check if there's an ongoing rebase +if (Test-Path ".git/rebase-merge") { + Write-Host "[FAIL] Rebase is not complete. There may be unresolved conflicts." -ForegroundColor Red + Write-Host "Hint: Resolve any conflicts, then use:" -ForegroundColor Yellow + Write-Host " git add " -ForegroundColor White + Write-Host " git rebase --continue" -ForegroundColor White + Write-Host "Or abort with: git rebase --abort" -ForegroundColor White + Set-Location .. + exit 1 +} + +# Get all commits on feature branch +$featureCommits = git log --oneline feature 2>$null +if (-not $featureCommits) { + Write-Host "[FAIL] No commits found on feature branch." -ForegroundColor Red + Set-Location .. + exit 1 +} + +# Check commit count (should be 5: 1 initial + 2 main + 2 feature) +$commitCount = (git rev-list --count feature 2>$null) +if ($commitCount -ne 5) { + Write-Host "[FAIL] Expected 5 commits on feature branch, found $commitCount" -ForegroundColor Red + Write-Host "Hint: The rebased feature branch should contain:" -ForegroundColor Yellow + Write-Host " - 1 initial commit" -ForegroundColor White + Write-Host " - 2 commits from main" -ForegroundColor White + Write-Host " - 2 commits from feature" -ForegroundColor White + Set-Location .. + exit 1 +} + +# Get commit messages in reverse chronological order (newest first) +$commits = git log --pretty=format:"%s" feature 2>$null + +# Convert to array and reverse to get chronological order (oldest first) +$commitArray = $commits -split "`n" +[array]::Reverse($commitArray) + +# Check that commits are in the expected order after rebase +$expectedOrder = @( + "Initial commit", + "Add installation instructions", + "Add configuration instructions", + "Add feature A", + "Add feature B" +) + +$orderCorrect = $true +for ($i = 0; $i -lt $expectedOrder.Length; $i++) { + if ($commitArray[$i] -ne $expectedOrder[$i]) { + $orderCorrect = $false + break + } +} + +if (-not $orderCorrect) { + Write-Host "[FAIL] Commit order is incorrect after rebase." -ForegroundColor Red + Write-Host "Expected order (oldest to newest):" -ForegroundColor Yellow + $expectedOrder | ForEach-Object { Write-Host " $_" -ForegroundColor White } + Write-Host "`nActual order:" -ForegroundColor Yellow + $commitArray | ForEach-Object { Write-Host " $_" -ForegroundColor White } + Write-Host "`nHint: The feature commits should come AFTER the main commits" -ForegroundColor Yellow + Set-Location .. + exit 1 +} + +# Check that main branch still has only 3 commits (initial + 2 main commits) +$mainCommitCount = (git rev-list --count main 2>$null) +if ($mainCommitCount -ne 3) { + Write-Host "[FAIL] Main branch should have 3 commits, found $mainCommitCount" -ForegroundColor Red + Write-Host "Hint: You should rebase feature onto main, not the other way around" -ForegroundColor Yellow + Set-Location .. + exit 1 +} + +# Check that there are no merge commits (parent count should be 1 for all commits) +$mergeCommits = git log --merges --oneline feature 2>$null +if ($mergeCommits) { + Write-Host "[FAIL] Found merge commits in history. Rebasing should create a linear history." -ForegroundColor Red + Write-Host "Hint: Use 'git rebase main' instead of 'git merge main'" -ForegroundColor Yellow + Set-Location .. + exit 1 +} + +# Verify that feature branch contains all commits from main +$mainCommits = git log --pretty=format:"%s" main 2>$null +$featureCommitMessages = git log --pretty=format:"%s" feature 2>$null + +foreach ($mainCommit in ($mainCommits -split "`n")) { + if ($featureCommitMessages -notcontains $mainCommit) { + Write-Host "[FAIL] Feature branch is missing commits from main." -ForegroundColor Red + Write-Host "Missing: $mainCommit" -ForegroundColor Yellow + 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 "- Rebased the feature branch onto main" -ForegroundColor White +Write-Host "- Created a clean, linear commit history" -ForegroundColor White +Write-Host "- Preserved all commits from both branches" -ForegroundColor White +Write-Host "`nYour feature branch now has a linear history!" -ForegroundColor Green +Write-Host "Run 'git log --oneline --graph --all' to see the result.`n" -ForegroundColor Cyan + +Set-Location .. +exit 0