How to: Git Interactive Rebase

🦋Allegra
6 min readJun 2, 2021

With Git you can rewrite your commit history so that it’s succinct, easy for others to review and tells a story.

This can involve changing the order of the commits, changing messages or modifying files in a commit, squashing together or splitting apart commits, or removing commits entirely — all before you share your work with others.

You’ll often want to do two things: change the commit message, or change the actual content of the commit by adding, removing and modifying files.

First, if you want to edit your commit history but have unstaged changes, git will tell you:

(base) ➜  coffee git:(722) ✗ git rebase -i HEAD~5 
error: cannot rebase: You have unstaged changes.
error: Please commit or stash them

You can stash them with:

git stash

And recover them when you need them (after rebase) with:

git stash apply

Now let’s do a rebase.

I have 4 commits in my feature branch.

Let’s say I noticed a typo in one of my commits and I also want to add a comment in another. I am going to add a commit called “Add comment to Timeout Interceptor” which I want to use to add a comment to my previous “Add timeout interceptor“ commit, and I am going to add a “Fix typo” commit to fix a typo in my previous commit called “Add request logging middleware”.

My previous commits are the way I want them, I am happy with them, I only want to add the minor changes to the 2 commits with the 2 new commits that I added.

You can check how many commits back do you need to go with git log. We need to go back 6 commits in this case.

Go back 6 commits with:

git rebase -i HEAD~6

This will start an interactive rebase.

On the top we have our 6 commits listed, each of them has a pick in front of them. Below are some of the commands we can use. But how can we use them?

pick e746fd3 Add timeout interceptor
pick df4b752 Add custom parseInt pipe
pick 10f08e4 Add request logging middleware
pick b42666f Add custom param decorator
pick 2758188 Add comment to Timeout Interceptor
pick 036a663 Fix typo
# Rebase b537dde..036a663 onto b42666f (6 commands)
#
# Commands:
# p, pick <commit> = use commit
# r, reword <commit> = use commit, but edit the commit message
# e, edit <commit> = use commit, but stop for amending
# s, squash <commit> = use commit, but meld into previous commit
# f, fixup <commit> = like "squash", but discard this commit's log message
# x, exec <command> = run command (the rest of the line) using shell
# b, break = stop here (continue rebase later with 'git rebase --continue')
# d, drop <commit> = remove commit
# l, label <label> = label current HEAD with a name
#
# These lines can be re-ordered; they are executed from top to bottom.

Move up with the keyboard arrow to place the cursor at the commit you want to move, in this instance it’s “Fix typo”. I want to add it to “Add request logging middleware” commit, that’s where the typo is. So I need to move it up and place it right under the “Add request logging middleware” commit.

How to: with the cursor on pick 036a663 Fix typo line enter yy on your keyboard (this will copy the line), then I move the cursor up to be on the line pick 10f08e4 Add request logging middleware (this is the commit I want to add the “Fix typo” commit to). I press p here (for paste), the commits list now looks like this:

pick e746fd3 Add timeout interceptor
pick df4b752 Add custom parseInt pipe
pick 10f08e4 Add request logging middleware
pick 036a663 Fix typo
pick b42666f Add custom param decorator
pick 2758188 Add comment to Timeout Interceptor
pick 036a663 Fix typo

Now I want to edit the command to be a fixup (f, fixup <commit> = like “squash”, but discard this commit’s log message) instead of a “pick”, because I want the changes to be part of “Add request logging middleware” commit and “Fix typo” message to disappear.

(If you want to keep the commit message use s (for “squash” instead).

To interact with your terminal interface click i on your keyboard (it stands for “insert”). You will see —- INSERT -— at the bottom of your terminal.

Delete “pick” and enter f (for fixup) on that line.

Move the cursor down to the duplicate “Fix typo” commit and enter d (for drop) on that line. This will delete the redundant copy of the commit. Now it will look like this:

pick e746fd3 Add timeout interceptor
pick df4b752 Add custom parseInt pipe
pick 10f08e4 Add request logging middleware
f 036a663 Fix typo
pick b42666f Add custom param decorator
pick 2758188 Add comment to Timeout Interceptor
d 036a663 Fix typo

Now we could save our changes and finish the interactive rebase, but remember, we have one more commit to fix!

It’s the “Add timeout interceptor“ commit.

I need to go out of the “insert” mode by pressing esc , copy the fixup commit with yy and paste it in the right place (cursor on the line of the commit I want to add it to) with p .

Go back to insert mode with i , add f on the fixup commit and d (for drop) on the one at the bottom.

Final is looking like:

pick e746fd3 Add timeout interceptor
f 2758188 Add comment to Timeout Interceptor
pick df4b752 Add custom parseInt pipe
pick 10f08e4 Add request logging middleware
f 036a663 Fix typo
pick b42666f Add custom param decorator
d 2758188 Add comment to Timeout Interceptor
d 036a663 Fix typo

Now we need to save the changes and leave the interactive rebase mode.

Press esc to exit the “insert” mode and enter :wq for save and exit (= write and quit)

If something didn’t go as planned type :q! to quit and exit (will not save any changes you made).

That’s it, all done! 🎉

Check your commit history with git log , you will only see the 4 commits and not a sight of “Fix typo” or “Add comment to Timeout Interceptor”, they are now part of the other two commits.

Push your change to remote with git push -f .

These are the most useful commands for me (I sometimes use to r too when I want to reword a commit message), but you can try others the same way.

That’s it for now! 🦋 Thanks for reading 😊

Other tips:

Other super easy way is to use VS Code’s “Undo last commit” feature if changes you want to make are in the last commit.

Another useful VS Code feature is to commit relevant code chunks together with “Stage selected ranges”. This is useful when you have all your changes and want to organise your commits by relevance and not by file (ie., one file has two different changes that you want to add to two separate commits with descriptive commit messages).

Photo by Devin H on Unsplash
Photo by Jaycee Xie on Unsplash

--

--