The brief
Take a branch with messy commits and clean it up using interactive rebase. The standard "polish before PR" workflow.
Step 1: Create a messy branch (10 min)
mkdir rebase-drill && cd rebase-drill
git init
echo "# Project" > README.md
git add . && git commit -m "Initial"
# Pretend it's main
git switch -c feature/login
# Messy commits
echo "function login() {}" > login.js
git add . && git commit -m "Add login function"
echo "function login(user, pass) {}" > login.js
git commit -am "WIP"
echo "function login(user, pass) { console.log('debug') }" > login.js
git commit -am "WIP add debug"
echo "function login(user, pass) { return validate(user, pass) }" > login.js
git commit -am "fix login"
echo "// Login module" > login.js
echo "function login(user, pass) { return validate(user, pass) }" >> login.js
git commit -am "Add comment"
echo "TODO: write tests" > tests.js
git add . && git commit -m "test stub"
git log --oneline
# Should show 6 commits with messy messages
Step 2: Interactive rebase (15 min)
git rebase -i main
Editor opens with:
pick abc1 Add login function
pick abc2 WIP
pick abc3 WIP add debug
pick abc4 fix login
pick abc5 Add comment
pick abc6 test stub
Edit to clean up:
pick abc1 Add login function
fixup abc2 WIP
fixup abc3 WIP add debug
fixup abc4 fix login
fixup abc5 Add comment
pick abc6 test stub
Save and close. Git replays:
- abc1 stays.
- abc2, abc3, abc4, abc5 are squashed INTO abc1 (their messages discarded).
- abc6 stays.
Result: 2 clean commits.
git log --oneline
# def4 (HEAD -> feature/login) test stub
# def5 Add login function
Step 3: Reword commit messages (10 min)
git rebase -i main
reword def5 Add login function
reword def4 test stub
Git prompts for each message in turn. Edit them:
Add login function with validation
Implements basic login: takes user and password, calls validate().
Add placeholder for login tests
TODO: write actual tests for the login function.
Now your commits have meaningful messages.
Step 4: Drop a commit (5 min)
Add a bad commit:
echo "secret_key=abc123" > .env
git add . && git commit -m "Add config (FORGOT THIS WAS A SECRET)"
Realize the mistake. Drop it via rebase:
git rebase -i main
Find the bad commit; change pick to drop:
pick xyz Add login function
pick xyz test stub
drop xyz Add config (FORGOT THIS WAS A SECRET)
Save. The commit and its changes are gone.
Verify:
git log --oneline # no bad commit
ls # no .env file
Step 5: Reorder commits (5 min)
Add a few more commits:
echo "login_button" > ui.js
git add . && git commit -m "Add login UI"
echo "throttle login" > rate_limit.js
git add . && git commit -m "Add login rate limit"
Rebase to reorder:
git rebase -i main
Change:
pick a Add login function
pick b test stub
pick c Add login UI
pick d Add login rate limit
To (reordering so rate limit comes before UI):
pick a Add login function
pick d Add login rate limit
pick c Add login UI
pick b test stub
If commits don't conflict, reorder works cleanly. If they do, resolve as usual.
Step 6: Autosquash workflow
Make some commits, then mark fixups for an earlier commit:
echo "// improved" >> login.js
git commit --fixup=<sha-of-earlier-login-commit>
echo "// more notes" >> login.js
git commit --fixup=<same-sha>
git log --oneline
# Shows: fixup! Add login function (etc.)
Auto-arrange in rebase:
git rebase -i --autosquash main
Git pre-arranges the fixups next to their targets in the editor. You just save.
Deliverables
Submission contract: upload CLEANUP.md (before/after git log --oneline, which rebase commands you used, when --autosquash helps).
100-pt rubric: messy branch created (10) / squash+fixup merged WIP into clean commits (25) / reword produced meaningful messages (20) / drop removed the secret commit (20) / reorder + --autosquash demonstrated (25).
upload CLEANUP.md (before/after git log --oneline, which rebase commands you used, when --autosquash helps).
messy branch created (10) / squash+fixup merged WIP into clean commits (25) / reword produced meaningful messages (20) / drop removed the secret commit (20) / reorder + --autosquash demonstrated (25).
upload CLEANUP.md (before/after git log --oneline, which rebase commands you used, when --autosquash helps).
messy branch created (10) / squash+fixup merged WIP into clean commits (25) / reword produced meaningful messages (20) / drop removed the secret commit (20) / reorder + --autosquash demonstrated (25).
- Repository with clean commit history (2-4 atomic commits, good messages).
- A
CLEANUP.mddescribing:- The before-and-after.
- What you used (
squash,fixup,drop,reword,edit, reorder). - When
--autosquashis useful.
What you'll learn
- The interactive rebase editor and commands.
squashvsfixup(fixup discards the message).- Dropping a commit via
dropor deleting the line. - Rewording vs editing (reword = message only; edit = stop for changes).
--autosquash+--fixup=SHAfor iterative cleanup.- The "polish before PR" mindset.
Stretch
- Try
editto actually split one commit into two (use git reset to un-commit, then commit selectively). - Use
--rootto rebase ALL commits including the first. - Practice on a branch with merge commits (use
--rebase-merges).