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

  1. Create a new practice repository:
mkdir rebase-practice && cd rebase-practice
git init
  1. 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"
  1. Verify your history:
git log --oneline

Your Mission

Using interactive rebase, clean up the last 6 commits to achieve:

  1. Combine related commits: Squash the three login test commits into one
  2. Combine related commits: Squash the two logout test commits into one
  3. Fix the typo: Change “add logout tset” to “add logout test”
  4. Reorder commits: Move all login-related commits before logout-related commits
  5. 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 (or r) to fix commit messages
  • Use squash (or s) to combine commits
  • Reorder lines to change commit order
  • Use fixup (or f) 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 with squash/fixup, rewording messages with reword, 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-is
    • reword changes commit messages
    • squash combines commits and lets you edit the message
    • fixup combines commits and discards the message
    • drop 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 or develop

🚀 Next Steps

What to Practice

  1. Daily workflow integration: Practice rebasing on your actual projects before creating pull requests
  2. Complex scenarios: Try rebasing with merge conflicts, splitting commits with edit, and combining rebasing with cherry-picking
  3. Team workflows: Experiment with rebase vs. merge strategies for different project types
  • 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 and git 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! 🎁