An Intro to Git Rebase

What is a fast-forward merge?

Lets say we create a new, empty Git repository. We do an initial commit on the master branch, then checkout a new branch, new_branch, and make a couple commits on it.

We might have a Git history that looks something like this:


new_branch is a couple of commits in front of master. If we checkout master and merge with new_branch, we get this:


master was “fast-forwarded” to get caught up with new_branch. No new commits were made. This happens by default when you merge a branch with another branch that can be reached by following the first branch’s commit history. There will never be conflicts in a fast-forward merge. To force a new commit you can use --no-ff

Lets say we push up our repo and another person begins to work on our project while we continue to work on it as well. Lets say we add an about page on our local new_branch and our co-worker adds a footer on her local new_branch and then pushes her changes up. We might see a Git history like the following:


We can see that new_branch and origin/new_branch have diverged. new_branch is our local new_branch that we are working from. origin/new_branch is also a local branch, but it tracks our remote server’s new_branch. A git fetch will update our local origin/new_branch to bring in any changes that have been pushed to the remote repo. Then, on new_branch we can do a git merge origin/new_branch to actually update our new_branch.


We have updated our local new_branch with all the latest changes but our local origin/new_branch is not fully up to date which means neither is the remote repo’s new_branch. We can push up our local new_branch, to update the remote repo.


When we merged origin/new_branch into our new_branch, we created an entirely new commit that was just for the merge. “Merge remote-tracking branch ‘origin/new_branch’ into new_branch” is the default message. Sometimes people change the message to something shorter like “merge fix”. It can be argued that this is a rather ugly commit that doesn’t really add much to our git history. Others may have no complaints and instead argue that being able to see the exact git history with merges included is ideal.

Lets look at an alternative to merge: rebase.

We need to add a products page and decide to create a new branch for it, products_page. We make some commits and our most recent Git history looks like this:


In the meantime, our co-worker has made some changes on the remote’s new_branch. After a git fetch, we can see these changes reflected in our origin/new_branch.


Lets update new_branch by checking it out and merging it with origin/new_branch.


This will result in a fast-forward merge – no new merge commits. This is because new_branch could be reached directly by following origin/new_branch‘s commit history.

We could merge products_page into new_branch but this would create a new merge commit while keeping the branching structure of our commit history. This is where the rebase option really starts to shine. Rebase lets us change the commit that a branch is based on.

Currently, our products_page branch is based on the merge commit. We created the products_page branch at the merge commit. Rebase allows us to take our products_page and plop it onto a different commit. We can do this by checking out products_page and running git rebase new_branch. The command git rebase <base> tells Git to place the branch that we are currently on, onto our <base>.


Rebasing is a great way to maintain a linear project history.  It is also a great way to integrate remote changes into your local repo. Rebasing is like saying, “I want to base my changes on what everybody has already done”.

We can update new_branch by checking it out and merging products_page into it. This will result in a fast-forward merge and no new merge commits. We can then delete our products_page branch. Finally, we can push new_branch to our remote to update origin/new_branch.


This is my favorite and most common use of rebase.

Rebase gets even cooler with its interactive option: git rebase -i <base>. This will open an editor where you have full control over how individual commits from your branch will be transferred to the new base. You can discard certain commits, change commit messages, split commits, combine commits, etc.

Lets take a look at a basic example and say that we did not yet rebase our deleted products_page branch onto new_branch. At that point, on products_page, we have a commit for “add products” and then a separate commit for “add more products”. It might be nice to combine those into one commit. We can do this by running:

git rebase -i HEAD~2

Our “base” here is the commit that is two back from the tip of our current branch. The “add products page skeleton” commit. We can edit all the commits that come after the base and then plop them all back onto our base. Here are the options we have when rebasing:

pick fa8238b add products
pick fedff0b add more products

# Rebase aae2cd4..fedff0b onto aae2cd4
# 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
#  x, exec = run command (the rest of the line) using shell
# These lines can be re-ordered; they are executed from top to bottom.
# If you remove a line here THAT COMMIT WILL BE LOST.
# However, if you remove everything, the rebase will be aborted.
# Note that empty commits are commented out

Note that the commits are listed from earliest to most recent.

squash fedff0b add more products

This will combine the two commits into one brand new commit, remove the two old commits and open a new editor that allows us to edit the commit message for the new commit. If you start an interactive rebase and realize that you shouldn’t be or are unsure of what to do next, you can always run git rebase --abort at any time within the process to cancel it.

We could have also used new_branch as our base and edited the commits on products_page right when we plopped it onto new_branch.

It is important to note that you should not rebase commits that you have pushed to a public repo. “Treat rebasing as a way to clean up and work with commits before you push them”.

Leave a comment below and follow me on twitter: @QuintonAiken