Git rebase: a quick explanation
According to StackOverflow’s annual developer survey, Git is the most popular version control system for developers, with almost 90% of them checking in their code via Git. As a regular user of Git myself, I find one particular Git command to be extremely powerful. That command is…
git rebase
What is it, really?
To quote the Pro Git book,
In Git, there are two main ways to integrate changes from one branch into another: the
merge
and therebase
.
You may have heard about or used git merge
. What about git rebase
?
To take it literally, “rebasing” means “to replace the base of something”. In this case, we’re replacing the “base” of some commits so that they are reapplied on top of another base tip. I believe it’s easier to understand if I demonstrate it, so here it is.
Let’s start with git merge
first. Let’s say you’ve created a new branch called
experiment
, and you’ve diverged your work by creating different commits on
experiment
and master
.
Now, let’s say you want to integrate your changes on experiment
into master
.
The easiest way to do this is to use git merge
. It will merge the two latest
branch states (“snapshots”, in this case, C3
and C4
) and the most recent
common ancestor of the two (C2
), creating a new snapshot and a merge
commit.
However, there’s another way. You can take the changes you’ve made on
experiment
and reapply them on top of C3
. That’s what we call a rebase
. In
this case, you can check out the experiment
branch and rebase it onto
master.
How to do it?
Here’s how:
$ git checkout experiment
$ git rebase master
First, rewinding head to replay your work on top of it...
Applying: C4
When you rebase, Git will go to the common ancestor of the two branches, saving
the changes you’ve made in each commit on experiment
into temporary files,
resetting experiment
to the same commit as master
, then reapplying each
commit in turn.
After that, you can go back to master
and do a git merge
, which will be a
fast-forward merge since the base of experiment
is now directly ahead of
master
.
$ git checkout master
$ git merge experiment
The snapshot of the end result pointed by C4'
is exactly the same as the one
pointed by C5
(in the first merge example). However, C4'
and C4
now have
different hashes because their parent commit is different. For C4
it’s C2
,
but for C4'
it’s C3
. If you’ve previously push
ed your experiment
branch
and you want to push
the rebase result, you’ll need to use --force
, because
you’ve rewritten the commit history.
Why all the hassle?
It’s true that using git merge
is easier than using git rebase
. So, why
should you use rebase
?
The answer is, you don’t have to. However, rebasing makes your commit history
much cleaner. If you look at the above examples, you’ll notice that the
rebase result looks like a linear history. It looks as if the work you did on
experiment
happened in series, even when it originally happened in parallel.
One would argue that rebasing is blasphemy because it rewrites your commit history and you’re lying about what actually happened. On the other hand, one could also argue that rebasing allows people to understand your project history more easily because there won’t be a lot of messy merge commits in it. It’s up to you to decide which side you’re on.
When to use it?
I’ll just quote the Pro Git book again.
Do not rebase commits that exist outside your repository and that people may have based work on.
If you follow that guideline, you’ll be fine. If you don’t, people will hate you, and you’ll be scorned by friends and family.
However, in my experience of contributing to open source projects on GitHub,
most project maintainers suggest using rebase
when you need to catch up with
the project’s master
branch. Therefore, the commit history of your Pull
Request will only contain your changes that would be incorporated into the
project, making it easier to review.
One particular part of git rebase
that I love is the --interactive
mode.
With the interactive mode, you can do different things such as amending,
squashing, or even dropping some commits when they are reapplied. This is very
useful if you made a mistake or just want to build your commit history
differently.
Another thing to note is that git rebase
can’t only be used to work with
branches. You can also pass a Git reference
or a commit hash as the argument instead of a branch. Therefore, you can use
something like git rebase HEAD~3 --interactive
to be able to modify up to the
last three commits from your current HEAD
. This is very useful when you need
to fix some mistakes in past commits, something I often do when working on my
local repository.
Now that you’ve learned the power of git rebase
, I just want to end this post
with:
Please use it wisely :)
Reference:
ProGit: Branching - Rebasing
Note:
This article was written as part of a series for my Software Engineering Project (Proyek Perangkat Lunak, PPL) course. This article was first published on Medium under a different title. I decided to write the whole series on my own blog, so I moved it here.