feat: add module 7 on rebasing

This commit is contained in:
Bjarke Sporring
2026-01-04 17:03:39 +01:00
parent da23059a69
commit b551eee54e
4 changed files with 429 additions and 0 deletions

View File

@@ -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 <branch-name>
# 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 <file>
# 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 <branch-name>
```
## 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.

View File

@@ -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"

View File

@@ -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

View File

@@ -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 <file>" -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