Introduction
Hello again! In our last lesson, we explored the high-level strategies of Git branching, concluding that modern, fast-paced development environments benefit most from workflows like GitHub Flow and Trunk-Based Development. A key principle of these models is maintaining a clean, understandable, and always-releasable main branch.
This lesson directly builds on that principle. A messy sequence of "WIP," "fix typo," and "oops, forgot a file" commits on a feature branch makes pull requests difficult to review and pollutes the project's history. Today, you'll master git rebase --interactive, the primary tool for transforming that messy history into a clean, logical narrative. By the end of this lesson, you will be able to confidently use interactive rebase to squash, reorder, and edit your commits, ensuring your contributions are professional and easy to understand.
Why Clean Commit History Matters
Before we dive into the commands, let's establish why this is a critical skill for a DevOps Engineer. A clean commit history isn't just about aesthetics; it has direct operational benefits:
- Faster Code Reviews: Reviewers can follow a logical story of how a feature was built, rather than wading through dozens of minor, unrelated changes.
- Easier Debugging: When a bug is introduced, a history of atomic, well-described commits makes it far easier to pinpoint the exact change that caused the problem (a skill we'll use later with
git bisect). - Simplified Rollbacks: Reverting a single, well-crafted feature commit is much safer and easier than trying to untangle a series of messy, interdependent commits.
The fundamental rule, which we touched on last time, is paramount: You should only rewrite history on commits that have not been shared with others. Interactive rebase is a tool for cleaning your local feature branch before you open a pull request or push it to a shared remote branch.
The Interactive Rebase Workflow
Interactive rebase allows you to pause the rebase process at each commit and decide what to do with it. Let's look at the most common operations.
To get a practical feel for this, the following video provides an excellent demonstration of the most common interactive rebase operations.
git interactive rebase - Undo, Edit & Squash git commits with a single command
This video from Philomatics walks through a realistic scenario, showing how to use git rebase -i to edit code in a past commit, split one commit into several, and squash multiple commits into one. Pay attention to the commands he uses and the workflow of starting, editing, and continuing the rebase.
Watch from the beginning (00:15) to 06:27. This covers the core operations we'll be discussing: editing, splitting (by resetting), squashing, reordering, and deleting commits.
Now, let's break down these operations with more detail, using a common scenario.
Imagine you're working on a feature and your commit history looks like this:
f7c3f3a - WIP: Started user autha4e5d8b - Added login endpointc8e9f0c - fix login typob2d1a5e - Added user profile page9f8d7c6 - Oops, forgot to add CSS file
This is a typical work-in-progress history. Our goal is to turn these five messy commits into two clean, logical ones:
Implement user authentication endpointAdd user profile page
1. Starting the Interactive Rebase
You start the process by telling Git how far back you want to edit. You can do this relative to HEAD or by specifying a base branch.
# Rebase the last 5 commits
git rebase -i HEAD~5
# Or, more commonly, rebase all commits on your feature branch that aren't in 'main'
git rebase -i main
This will open your configured text editor with a "to-do" list for the rebase.

The file will look something like this (note the reversed order from git log):
pick f7c3f3a WIP: Started user auth
pick a4e5d8b Added login endpoint
pick c8e9f0c fix login typo
pick b2d1a5e Added user profile page
pick 9f8d7c6 Oops, forgot to add CSS file
# Commands:
# p, pick = use commit
# r, reword = use commit, but edit the commit message
# e, edit = use commit, but stop for amending
# s, squash = use commit, but meld into previous commit
# f, fixup = like "squash", but discard this commit's log message
# d, drop = remove commit
...
2. Squashing and Rewording Commits
Our first goal is to combine the first three commits into a single one. We'll use pick, squash, and reword.
pick: Use the commit as-is.squash: Combine this commit's changes with the one above it. Git will then prompt you to combine the commit messages.reword: Use the commit's changes but pause to let you edit the commit message.
We can edit the file to look like this:
pick f7c3f3a WIP: Started user auth
squash a4e5d8b Added login endpoint
squash c8e9f0c fix login typo
pick b2d1a5e Added user profile page
fixup 9f8d7c6 Oops, forgot to add CSS file
Wait, this isn't quite right. The first commit f7c3f3a has a bad message. A better approach is to reword it. And the last commit 9f8d7c6 is just a fix for the profile page, so we can use fixup to meld it and discard its message automatically.
Here is the correct plan:
reword f7c3f3a WIP: Started user auth
squash a4e5d8b Added login endpoint
squash c8e9f0c fix login typo
pick b2d1a5e Added user profile page
fixup 9f8d7c6 Oops, forgot to add CSS file
When you save and close this file, Git will execute the plan:
- It will first pause and let you reword the message for
f7c3f3a. You'll changeWIP: Started user authtoImplement user authentication endpoint. - Then, it will pause again to let you combine the messages for the two squashed commits. You can clean this up to be part of the main commit message.
- It will pick the
b2d1a5ecommit. - Finally, it will automatically fixup
9f8d7c6into the previous commit (b2d1a5e) without asking for message input.

3. Editing and Reordering Commits
What if you realized you left a debug statement (console.log, print(), etc.) in the Added login endpoint commit?
edit: This command tells Git to pause at that commit and drop you back into your terminal. You can then amend the commit (git commit --amend), or even split it into multiple commits.
Let's say you wanted to reorder the commits, perhaps because the profile page should have been committed before the login endpoint. You can simply change the order of the lines in the interactive editor.
Original Plan:
pick a4e5d8b Added login endpoint
pick b2d1a5e Added user profile page
New Plan (Reordered):
pick b2d1a5e Added user profile page
pick a4e5d8b Added login endpoint
Warning: Reordering commits that depend on each other will likely cause merge conflicts, which you'll have to resolve.
For a great written reference that includes command tables and a full workflow example, refer to the following guide.
How to Handle Git Interactive Rebase
The article 'How to Handle Git Interactive Rebase' from OneUptime is an excellent and comprehensive guide. We'll use it as a reference for a complete workflow and for handling conflicts.
First, read the sections 'Basic Interactive Rebase' and 'Common Rebase Operations'. Then, study the 'Workflow Example: Cleaning Up Before PR' section. This mirrors the scenario we've just discussed and reinforces the concepts.
Handling Conflicts and Recovering from Mistakes
Interactive rebase is powerful, but because it rewrites history, things can sometimes go wrong.
Conflicts: As Git reapplies each commit, you might encounter a merge conflict. The process will pause, and you'll see the standard conflict markers (<<<<<<<, =======, >>>>>>>) in your files.
- Open the conflicted files and resolve the issues.
- Stage the resolved files with
git add <filename>. - Continue the rebase with
git rebase --continue. - If you get overwhelmed, you can always abort with
git rebase --abortto return to the state before you started.
Recovery with reflog: What if you accidentally drop a commit you needed or mess up a squash? Git keeps a log of all the states your HEAD has been in. git reflog is your ultimate safety net.
$ git reflog
a1b2c3d HEAD@{0}: rebase -i (finish): returning to refs/heads/my-feature
a1b2c3d HEAD@{1}: rebase -i (squash): Implement user authentication
e5f6g7h HEAD@{2}: rebase -i (squash): # This is a combination of 3 commits.
f7c3f3a HEAD@{3}: rebase -i (start): checkout main
b2d1a5e HEAD@{4}: commit: Added user profile page <-- The state before the rebase
...
If you realize the rebase was a mistake, you can simply reset your branch back to its previous state:
git reset --hard HEAD@{4}
Or, if you just lost one commit, you can find its hash in the reflog and git cherry-pick <hash> it back into your branch.
The "Common Mistakes and Recovery" section in the OneUptime article provides a good summary of this process.
Conclusion
Mastering interactive rebase is a significant step up in your Git proficiency. It moves you from just recording history to actively curating it, a practice that pays huge dividends in team velocity and code quality.
Key Takeaways:
- Interactive rebase is used to clean up your local commit history before sharing it.
- The goal is to create a logical, reviewable story with atomic commits.
- The most common operations are
squash(combine commits and messages),fixup(combine and discard message),reword(edit message),edit(amend commit content), anddrop(delete commit). - You can resolve conflicts during a rebase with the standard
git addandgit rebase --continueworkflow. git reflogis your crucial safety net for recovering from any mistakes.
Preview of the Next Lesson:
Now that you can craft the perfect commit, what happens when you need to apply that single, perfect change to a different branch? For example, backporting a critical bug fix from main to an older release-v1.2 branch without bringing along other unrelated changes. In our next lesson, we will cover git cherry-pick, the tool designed for exactly this scenario.