Skip to main content

Welcome to a Grasp lesson

Create a free account to keep studying and create your own study path.

Lesson 2

Interactive Rebase: Mastering Your Git History

Recommended resources

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:

  1. f7c3f3a - WIP: Started user auth
  2. a4e5d8b - Added login endpoint
  3. c8e9f0c - fix login typo
  4. b2d1a5e - Added user profile page
  5. 9f8d7c6 - 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:

  1. Implement user authentication endpoint
  2. Add 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.

Git Interactive Rebase Session
This is what the interactive rebase editor looks like. It lists the commits (oldest at the top) and provides a cheat sheet of commands you can use for each one.

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:

  1. It will first pause and let you reword the message for f7c3f3a. You'll change WIP: Started user auth to Implement user authentication endpoint.
  2. 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.
  3. It will pick the b2d1a5e commit.
  4. Finally, it will automatically fixup 9f8d7c6 into the previous commit (b2d1a5e) without asking for message input.
Git Squash Visualization
This diagram illustrates the concept of squashing. Multiple commits from a feature branch are consolidated into a single, more meaningful commit on the main branch, creating a cleaner history.

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.

  1. Open the conflicted files and resolve the issues.
  2. Stage the resolved files with git add <filename>.
  3. Continue the rebase with git rebase --continue.
  4. If you get overwhelmed, you can always abort with git rebase --abort to 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), and drop (delete commit).
  • You can resolve conflicts during a rebase with the standard git add and git rebase --continue workflow.
  • git reflog is 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.