Interactive rebase is a one-stop shop for revising history: combining, removing and reordering commits. It’s one of those things  where you learn it, and afterwards wonder how you got by without knowing  it. I use it almost every day when preparing a topic branch for review.  If I have to make changes during review, I commit them as incremental  patches, then squash them with an interactive rebase before I commit.

In this post, I’ll explain a bit about how it works. Let’s forget  about topic branches right now, and just assume you’re working directly  on the master.

Say you’ve been working a bit, and you have a recent history that looks like this (most recent to oldest):

  • master Fix a bug when you foo the bar
  • Fix typo
  • Implement the remainder of feature XYZ
  • Oops, forgot to add this file
  • Implement part of feature XYZ
  • origin/master (Ancient history that’s in the upstream repo)

We’ve added five commits here, so we want to run the following command:

git rebase -i HEAD~5

(If we were using a topic branch, we could do git rebase -i master — this also has the effect of rebasing the topic branch if the master has been updated)

This will open up your editor, with something that looks like this:

pick 39a2bef Implement part of feature XYZ
pick 4bc987e Oops, forgot to add this file
pick f9282bc Implement the remainder of feature XYZ
pick 2c5dd33 Fix typo
pick a524b5c Fix a bug when you foo the bar

On each line, you have the command (“pick” here), the commit ID, and  the description. At the bottom of the file, you’ll see a list of  commands you can use:

  • pick — leave the commit as-is
  • reword — change the commit summary only (this is a recent addition)
  • edit — change the commit contents, much like doing a git reset –mixed
  • squash — keep the changes  in this commit, but merge it with the commit on the previous line, and  append the commit messages and allow the message to be edited.
  • fixup — keep the changes, as with squash but throw away the log message.

You can also delete a line to discard a commit entirely, or reorder  them. The idea is that you edit this file so it shows the history you  wish you created, then write the file and close your editor. Let’s  change it so it looks like this:

pick 39a2bef Implement part of feature XYZ
fixup 4bc987e Oops, forgot to add this file
pick f9282bc Implement the remainder of feature XYZ
fixup 2c5dd33 Fix typo
fixup a524b5c Fix a bug when you foo the bar

I’ve just changed “pick” to “fixup”  for all the mistake commits I made. This will roll them in with the  commits above them so that, when we close the editor, git will re-write  your history so it looks like this:

  • Implement the remainder of feature XYZ
  • Implement part of feature XYZ
  • (Ancient history)

Mistakes? What mistakes? :)

As I said, I use this almost daily to prepare a patch set for review. After I rebase, I’d prepare patch emails with git format-patch -2 --attach, then send them with git send-email 000[12]* --to=mailinglist@somewhere.com.

Hope that helps you out. Rewriting history like this is a key feature  of git that I haven’t seen in other source control systems, and it goes  a long way towards making your project history look professional.