Module 5: Rebasing: Creating Clean Test History
Master rebasing to maintain a linear, clean history in your test automation projects. Learn when rebasing is preferable to merging, how to rebase feature branches onto updated main branches, and practice interactive rebasing to clean up test commits before sharing with the team.
Interactive Rebase: Cleaning Up Test Commit History
Why This Matters
Picture this: You’ve spent the past week developing a comprehensive test suite for a new API feature. Your Git history shows the reality of your development process: 47 commits including “WIP - fixing flaky test”, “oops forgot assertion”, “actually fix test this time”, and “removing debug logging (for real now)”. While this messy history accurately reflects your iterative process, it’s not what you want to share with your team.
In test automation projects, clean commit history is crucial because:
- Code Review Efficiency: Reviewers can understand your test strategy when commits are logically organized (“Add user authentication tests” vs. “fix typo in test #3”)
- Debugging Test Failures: When tests break in production, a clean history helps you quickly identify which test changes introduced issues
- Knowledge Transfer: New team members understand test coverage evolution through well-structured commit history
- CI/CD Pipeline Clarity: Clean commits make it easier to trace which test changes triggered pipeline failures
Common pain points this lesson addresses:
- Embarrassing commit messages (“asdfasdf”, “I hate this test”) being shared with the entire team
- Dozens of “fix lint errors” commits cluttering your pull request
- Difficulty tracking the logic behind test implementation due to fragmented commits
- Merge commits creating a tangled, non-linear history that’s hard to navigate
- Accidental inclusion of debug code or sensitive test data in commit history
When you’ll use these skills:
- Before creating pull requests for test automation code
- When synchronizing your feature branch with updates from the main branch
- After experimental testing work that resulted in many small, incremental commits
- When collaborating on test frameworks where clean history is a team standard
- During test refactoring projects where you want to present logical, reviewable changes
What You’ll Accomplish
By the end of this lesson, you’ll transform from making messy, stream-of-consciousness commits to crafting professional, story-like commit histories that your team will appreciate.
Here’s how we’ll cover each learning objective:
Understanding Rebase vs Merge: We’ll start by examining two identical scenarios—one using merge, one using rebase—so you can visually see the difference in resulting history. You’ll learn the specific situations in test automation where rebasing provides clearer, more maintainable project history.
Rebasing Feature Branches: You’ll practice the essential skill of updating your test branch with the latest changes from main. We’ll walk through the complete rebase workflow, including what happens when your test branch needs recent framework updates before you can merge.
Interactive Rebase Operations: This is where the magic happens. You’ll learn the interactive rebase commands to:
- Squash multiple “fix test” commits into one coherent commit
- Reorder commits so test setup appears before test implementation
- Edit commit messages to clearly describe what tests cover
- Drop commits containing debug code you never meant to share
- Fixup commits to silently combine them without editing messages
Pre-Pull Request Cleanup: We’ll simulate a real pull request scenario where you clean up a week’s worth of messy test commits into 3-5 logical, well-documented commits that tell the story of your test implementation.
Conflict Resolution During Rebase: You’ll encounter and resolve conflicts that occur during rebasing—a critical skill since test files often touch the same areas. We’ll cover safe strategies for handling conflicts without losing work.
By the end, you’ll have hands-on experience transforming chaotic commit histories into professional, reviewable changes—a skill that will make you a more effective and respected member of any test automation team.
Core Content
Core Content: Interactive Rebase - Cleaning Up Test Commit History
Core Concepts Explained
What is Interactive Rebase?
Interactive rebase (git rebase -i
) is a powerful Git tool that allows you to modify, reorder, combine, or delete commits in your repository’s history. For test automation projects, this is invaluable for cleaning up messy commit histories before merging your test code into the main branch.
Why Clean Up Test Commit History?
When developing automated tests, you often create many small commits like:
- “Added login test”
- “Fixed typo in test”
- “Actually fixed the typo”
- “Removed debug statement”
These commits clutter your history. Interactive rebase lets you combine them into a single, meaningful commit like “Add comprehensive login authentication tests.”
Understanding the Interactive Rebase Workflow
graph TD
A[Start: Multiple messy commits] --> B[Run git rebase -i]
B --> C[Choose actions: pick, reword, squash, fixup, drop]
C --> D[Edit commit messages if needed]
D --> E[Resolve conflicts if any]
E --> F[Complete: Clean commit history]
Key Interactive Rebase Commands
Command | Action | Use Case |
---|---|---|
pick |
Keep commit as-is | Commits that are already good |
reword |
Change commit message | Fixing unclear messages |
squash |
Combine with previous commit, keep both messages | Merging related work |
fixup |
Combine with previous commit, discard message | Fixing small errors |
drop |
Remove commit entirely | Deleting unnecessary commits |
edit |
Pause to modify commit | Splitting or editing commits |
Practical Code Examples
Example 1: Starting Interactive Rebase for Your Test Suite
Let’s say you’ve been working on test automation for practiceautomatedtesting.com and have these commits:
$ git log --oneline
a1b2c3d Add checkout test debugging
e4f5g6h Fix checkout test selector
h7i8j9k Add checkout test
k0l1m2n Add login test
Step 1: Start interactive rebase for the last 4 commits:
# Rebase the last 4 commits
$ git rebase -i HEAD~4
# Alternative: Rebase from a specific commit
$ git rebase -i k0l1m2n^
Step 2: The interactive editor opens:
pick k0l1m2n Add login test
pick h7i8j9k Add checkout test
pick e4f5g6h Fix checkout test selector
pick a1b2c3d Add checkout test debugging
# Rebase instructions:
# p, pick = use commit
# r, reword = use commit, but edit the commit message
# s, squash = use commit, but meld into previous commit
# f, fixup = like "squash", but discard this commit's log message
# d, drop = remove commit
Example 2: Squashing Test Fixes into Main Commits
Scenario: You want to combine the checkout test fixes into the original commit.
Step 1: Modify the rebase instructions:
pick k0l1m2n Add login test
pick h7i8j9k Add checkout test
fixup e4f5g6h Fix checkout test selector
fixup a1b2c3d Add checkout test debugging
Step 2: Save and close the editor
Git will automatically combine the commits. Your history now looks like:
$ git log --oneline
x9y8z7w Add checkout test
k0l1m2n Add login test
Example 3: Complete Test Automation Cleanup
Let’s work through a realistic test automation scenario with actual test code:
# Your messy commit history
$ git log --oneline -8
1a2b3c4 Remove console.log from booking test
2b3c4d5 Fix date selector in booking test
3c4d5e6 Add booking form test
4d5e6f7 WIP: trying different approach
5e6f7g8 Add room selection test
6f7g8h9 Typo fix
7g8h9i0 Add hotel search test
8h9i0j1 Initial test framework setup
Step 1: Start interactive rebase:
$ git rebase -i HEAD~7
# (Don't rebase the framework setup)
Step 2: Edit the rebase plan:
pick 7g8h9i0 Add hotel search test
fixup 6f7g8h9 Typo fix
pick 5e6f7g8 Add room selection test
drop 4d5e6f7 WIP: trying different approach
pick 3c4d5e6 Add booking form test
squash 2b3c4d5 Fix date selector in booking test
squash 1a2b3c4 Remove console.log from booking test
Step 3: When squashing, you’ll be prompted to combine messages:
# This is a combination of 3 commits.
# The first commit's message is:
Add booking form test
# This is the 2nd commit message:
Fix date selector in booking test
# This is the 3rd commit message:
Remove console.log from booking test
# Write your combined message:
Step 4: Write a comprehensive commit message:
Add comprehensive booking form validation tests
- Implemented date selection functionality
- Added form field validation
- Tested complete booking flow from search to confirmation
- Covered edge cases for invalid date ranges
Final clean history:
$ git log --oneline -4
9z8y7x6 Add comprehensive booking form validation tests
5e6f7g8 Add room selection test
a1b2c3d Add hotel search test
8h9i0j1 Initial test framework setup
Example 4: Reordering Commits
Sometimes you want to reorder commits logically:
# Current order (chronological)
pick a1a1a1a Add payment test
pick b2b2b2b Add login test
pick c3c3c3c Add registration test
# Logical order (feature flow)
pick c3c3c3c Add registration test
pick b2b2b2b Add login test
pick a1a1a1a Add payment test
Example 5: Rewording Commit Messages
Make your test commit messages more descriptive:
pick d4d4d4d test update
reword e5e5e5e fix
pick f6f6f6f new tests
When you mark a commit for reword
, Git will pause and let you edit:
# Original
fix
# Improved
Fix flaky test timeout in checkout flow
Increased wait time for payment gateway response from 5s to 10s
to handle slow network conditions during CI runs.
Handling Conflicts During Rebase
When rebasing, you may encounter merge conflicts:
$ git rebase -i HEAD~3
Auto-merging tests/checkout.spec.js
CONFLICT (content): Merge conflict in tests/checkout.spec.js
error: could not apply e4f5g6h... Fix checkout test
Resolution steps:
# 1. Check the conflict
$ git status
rebase in progress; onto a1b2c3d
You are currently rebasing branch 'feature/checkout-tests' on 'a1b2c3d'.
Unmerged paths:
both modified: tests/checkout.spec.js
# 2. Open the file and resolve conflicts (look for <<<<<<<, =======, >>>>>>>)
# 3. Stage the resolved file
$ git add tests/checkout.spec.js
# 4. Continue the rebase
$ git rebase --continue
# If you want to abort and start over:
$ git rebase --abort
Interactive Rebase Safety Practices
Before You Start
# 1. Always work on a feature branch, never main
$ git checkout -b feature/cleanup-test-history
# 2. Create a backup branch
$ git branch backup-before-rebase
# 3. Ensure working directory is clean
$ git status
On branch feature/cleanup-test-history
nothing to commit, working tree clean
Pushing After Rebase
# First time after rebase (rewrites history)
$ git push origin feature/cleanup-test-history --force-with-lease
# --force-with-lease is safer than --force
# It ensures you don't overwrite others' work
Warning: Never rebase commits that have been pushed to a shared/public branch!
graph LR
A[Local Only Commits] -->|Safe to Rebase| B[Clean History]
C[Pushed to Shared Branch] -->|DO NOT REBASE| D[Leave As-Is]
C -->|Alternative| E[Create New Commits]
Common Mistakes Section
Mistake 1: Rebasing Public/Shared Commits
Problem:
# Rebasing commits already on main branch
$ git checkout main
$ git rebase -i HEAD~5 # DON'T DO THIS!
Solution: Only rebase commits on your feature branches that haven’t been shared with others.
Mistake 2: Losing Important Commits with Wrong Commands
Problem: Using drop
or fixup
when you meant pick
or squash
Solution:
# If you haven't pushed yet:
$ git reflog # Find your previous commit
$ git reset --hard HEAD@{3} # Go back to before rebase
# If you created a backup:
$ git reset --hard backup-before-rebase
Mistake 3: Not Resolving Conflicts Properly
Problem: Continuing rebase without properly testing after conflict resolution
Solution:
# After resolving conflicts and before continuing:
$ npm test # Run your test suite
$ git add .
$ git rebase --continue
Mistake 4: Confusing Squash and Fixup
Squash: Keeps all commit messages (you can edit the combined message)
pick abc1234 Add login test
squash def5678 Add logout test
# Result: You'll edit a message combining both
Fixup: Discards the fixup commit’s message
pick abc1234 Add login test
fixup def5678 Fix typo in login test
# Result: Only "Add login test" message remains
Mistake 5: Forgetting to Test After Rebase
Always run your test suite after rebasing:
$ git rebase -i HEAD~5
# ...complete rebase...
$ npm test # or your test command
$ npm run test:e2e
Debugging Interactive Rebase Issues
# Check rebase status
$ git status
# View what's happening
$ git log --oneline --graph
# If things go wrong, abort
$ git rebase --abort
# Check reflog to recover lost commits
$ git reflog
$ git cherry-pick <commit-hash> # Recover specific commits
Hands-On Practice
EXERCISE
Hands-On Exercise: Clean Up a Messy Test Suite Commit History
Scenario
You’ve been working on a test automation suite and made several commits while fixing bugs and adding tests. Your commit history is messy with typos, WIP commits, and changes that should be grouped together. Before merging to main, you need to clean it up using interactive rebase.
Task
Use interactive rebase to transform a chaotic commit history into a clean, logical sequence that tells a clear story of your test automation work.
Setup Instructions
- Create a new practice repository:
mkdir rebase-practice && cd rebase-practice
git init
- Create the messy commit history:
# Initial test file
echo "describe('Login Tests', () => {});" > login.test.js
git add login.test.js
git commit -m "Initial commit"
# Commit 1
echo "describe('Login Tests', () => {
it('should login with valid credentials', () => {});
});" > login.test.js
git add login.test.js
git commit -m "add login test"
# Commit 2
echo "describe('Login Tests', () => {
it('should login with valid credentials', () => {});
it('should show error with invalid password', () => {});
});" > login.test.js
git add login.test.js
git commit -m "WIP"
# Commit 3
echo "describe('Login Tests', () => {
it('should login with valid credentials', () => {});
it('should show error with invalid password', () => {});
it('should show error with invalid email', () => {});
});" > login.test.js
git add login.test.js
git commit -m "forgot to add this"
# Commit 4
echo "describe('Logout Tests', () => {
it('should logout successfully', () => {});
});" > logout.test.js
git add logout.test.js
git commit -m "add logout tset"
# Commit 5
echo "describe('Logout Tests', () => {
it('should logout successfully', () => {});
it('should clear session data', () => {});
});" > logout.test.js
git add logout.test.js
git commit -m "add session test"
# Commit 6
echo "describe('Login Tests', () => {
it('should login with valid credentials', () => {});
it('should show error with invalid password', () => {});
it('should show error with invalid email', () => {});
it('should handle empty fields', () => {});
});" > login.test.js
git add login.test.js
git commit -m "oops missed validation test"
- Verify your history:
git log --oneline
Your Mission
Using interactive rebase, clean up the last 6 commits to achieve:
- Combine related commits: Squash the three login test commits into one
- Combine related commits: Squash the two logout test commits into one
- Fix the typo: Change “add logout tset” to “add logout test”
- Reorder commits: Move all login-related commits before logout-related commits
- Write clear commit messages: Ensure final messages follow best practices
Step-by-Step Instructions
Step 1: Start Interactive Rebase
git rebase -i HEAD~6
Step 2: In the editor, you’ll see:
pick abc1234 add login test
pick def5678 WIP
pick ghi9012 forgot to add this
pick jkl3456 add logout tset
pick mno7890 add session test
pick pqr1234 oops missed validation test
Step 3: Modify the rebase plan:
- Use
reword
(orr
) to fix commit messages - Use
squash
(ors
) to combine commits - Reorder lines to change commit order
- Use
fixup
(orf
) for commits you want to squash without editing messages
Step 4: Save and close the editor
Step 5: Edit commit messages when prompted
Step 6: Verify your cleaned history:
git log --oneline
Expected Outcome
Your final history should look like:
abc1234 Add comprehensive login tests with validation
def5678 Add logout tests with session clearing
ghi9012 Initial commit
Each commit should:
- Have a clear, descriptive message
- Group related changes together
- Follow conventional commit message format
- Tell a logical story of development
Solution Approach
Click to reveal solution
Interactive rebase commands:
pick abc1234 add login test
squash def5678 WIP
squash ghi9012 forgot to add this
fixup pqr1234 oops missed validation test
reword jkl3456 add logout tset
squash mno7890 add session test
Alternative approach (with reordering):
pick abc1234 add login test
fixup def5678 WIP
fixup ghi9012 forgot to add this
fixup pqr1234 oops missed validation test
reword jkl3456 add logout tset
squash mno7890 add session test
New commit messages:
- For login tests: “Add comprehensive login tests with validation”
- For logout tests: “Add logout tests with session clearing”
Verification commands:
git log --oneline
git show HEAD
git show HEAD~1
CONCLUSION
🎓 Key Takeaways
-
Interactive rebase is your history editor: Use
git rebase -i
to rewrite commit history before sharing code, combining commits withsquash
/fixup
, rewording messages withreword
, and reordering commits by moving lines -
Clean history improves team collaboration: Well-organized commits make code reviews easier, help teammates understand changes, simplify debugging with
git bisect
, and make reverting changes safer -
Common rebase commands serve different purposes:
pick
keeps commits as-isreword
changes commit messagessquash
combines commits and lets you edit the messagefixup
combines commits and discards the messagedrop
removes commits entirely
-
Safety first with rebasing: Only rebase commits that haven’t been pushed to shared branches, use
git reflog
as a safety net to recover from mistakes, and test your code after rebasing to ensure nothing broke -
Timing matters: Rebase before pushing to remote, before creating pull requests, and when cleaning up feature branches—but never rebase commits on shared branches like
main
ordevelop
🚀 Next Steps
What to Practice
- Daily workflow integration: Practice rebasing on your actual projects before creating pull requests
- Complex scenarios: Try rebasing with merge conflicts, splitting commits with
edit
, and combining rebasing with cherry-picking - Team workflows: Experiment with rebase vs. merge strategies for different project types
Related Topics to Explore
- Git reflog: Master recovering from rebase mistakes
- Commit message conventions: Learn Conventional Commits format for better history
- Git bisect: Use clean history for efficient bug hunting
- Force push safely: Understand
--force-with-lease
when updating rebased branches - Merge strategies: Compare rebase vs. merge workflows for team collaboration
- Autosquash: Use
git commit --fixup
andgit rebase -i --autosquash
for efficient workflows
📚 Additional Resources
- Practice in a safe environment with git-school or learngitbranching.js.org
- Set up git aliases for common rebase operations
- Read your team’s contribution guidelines about commit history expectations
Remember: A clean commit history is a gift to your future self and your team. Take the time to craft it well! 🎁