Everything You Need to Know
The Git rebase command moves a branch to a new location at the head of another branch. Unlike the Git merge command, rebase involves rewriting your project history. It’s a great tool, but don’t rebase commits other developers have based work on.
The Git rebase
command combines two source code branches into one. The Git merge
command does that too. We explain what rebase
does, how it’s used, and when to use merge
instead.
The Git Explosion
Frustrated with other version control systems and their slow updates and commits, Linus Torvalds, of Linux kernel fame, put aside a month in 2005 to write his own. He named it Git.
Sites like GitHub, GitLab, and BitBucket have symbiotically promoted and benefited from Git. Today Git is used globally, with a massive 98 percent of 71 thousand respondents in a 2022 survey using Git as the version control system.
One of Git’s main design decisions was speed. In particular, working with branches had to be as fast as possible. Branches are a fundamental part of version control systems. A project repository will have a main or master branch. This is where the project’s code base sits. Development, such as new features, takes place in segregated side branches. This stops the work done in branches from messing up the master branch, and it allows simultaneous development to happen in different parts of the code base.
As the developments in the side branches are completed, the changes are transferred to the master branch by merging the development branch into the master branch. In other version controls systems working with branches was difficult and computationally expensive. Working with branches in Git is very fast, and very lightweight. What was once a tedious and often avoided exercise in other systems, became trivial in Git.
The Git rebase
command is another way of transferring the changes from one branch into another branch. The merge
and rebase
commands have similar objectives, but they achieve their ends in different ways and yield slightly different results.
What Is Git merge?
So what is the Git merge
command for? Let’s say you’ve created a branch called dev-branch
to work on a new feature.
You make a few commits, and test your new feature. It all works well. Now you want to send your new feature to the master
branch. You must be in the master
branch to merge another to it.
We can ensure we’re in the master
branch by explicitly checking it out before we merge.
git checkout master
We can now tell Git to merge the dev-branch
into the current branch, which is the master
branch.
git merge dev-branch
Our merge
is completed for us. If you checkout the master
branch and compile it, it’ll have the newly developed feature in it. What Git has actually performed is a three-way merge. it compares the most recent commits in the master
and dev-branch
branches, and the commit in the master
branch immediately before the dev-branch
was created. It then performs a commit on the master
branch.
Merges are considered nondestructive because they don’t delete anything and they don’t change any of the Git history. The dev-branch
still exists, and none of the previous commits are altered. A new commit is created that captures the results of the three-way merge.
After the merge, our Git repository looks like a timeline with an alternative line branching off and then returning to the main timeline.
The dev-branch
branch has been incorporated into the master
branch.
If you have a lot of branches in one project, the history of the project can become confusing. This is often the case if a project has many contributors. Because the development effort splits into many different paths, the development history is non-linear. Untangling the commit history becomes even more difficult if branches have their own branches.
Note that if you have uncommitted changes in the master
branch, you’ll need to do something with these changes before you can merge anything to it. You could create a new branch and commit the changes there, and then do the merge. You’d then need to merge your temporary branch back into the master branch.
That works, but Git has a command that achieves the same thing, without having to create new branches. The stash
command stores your uncommitted changes for you, and lets you call them back with stash pop
.
You’d use them like this:
stash git merge dev-branch stash pop
The end result is a merged branch, with your unsaved changes restored.
What Is Git rebase?
Taking our previous example, before we performed any action our Git repository looks like this. We have a branch called dev-branch
and we want to move those changes to the master
branch.
After the rebase
, it looks like a single, completely linear timeline of changes.
The dev-branch
has been removed, and the commits in the dev-branch
have been added to the master branch. The end result is the same as if the commits in the dev-branch
had actually been directly committed to the master
branch in the first place. The commits aren’t just tacked onto the master
branch, they’re “replayed” and added fresh.
This is why the rebase
command is considered destructive. The rebased branch no longer exists as a separate branch, and the Git history of your project has been rewritten. You can’t determine at some later point which commits were originally made to the dev-branch
.
However, it does leave you with a simplified, linear, history. Compared to a repository with dozens or even hundreds of branches and merges, reading the Git log or using a graphical git GUI to look at a graph of the repository, a rebased repository is a breeze to understand.
How to Rebase Onto Another Branch
Let’s try a git rebase
example. We’ve got a project with a branch called new-feature
. We’d rebase
that branch onto the master
branch like this.
First, we check that the master
branch has no outstanding changes.
git status
We checkout the new-feature
branch.
git checkout new-feature
We tell Git to rebase
the current branch onto the master branch.
git rebase master
We can see that we have still got two branches.
git branch
We swap back to the master
branch
git checkout master
We merge the new-feature branch into the current branch, which in our case is the master
branch.
git merge new-feature
Interestingly, we’ve still got two branches after the final merge.
The difference is, now the head of the new-feature
branch and the head of the master
branch are set to point to the same commit, and the Git history doesn’t show there used to be a separate new-feature
branch, apart from the branch label.
Git Rebase vs. Merge: Which One Should You Use?
It’s not a case of rebase
vs. merge
. They’re both powerful commands and you’ll probably use them both. That said, there are use cases where rebase
doesn’t really work that well. Unpicking mistakes caused by mistakes using merge
are unpleasant, but unpicking errors caused by rebase
is hellish.
If you’re the only developer using a repository, there’s less chance of you doing something with rebase
that is disastrous. You could still rebase
in the wrong direction for example, and rebase
your master branch onto your new-feature
branch. To get your master
branch back, you’d need to rebase
again, this time from your new-feature
branch to your master
branch. That would restore your master
branch, albeit with an odd-looking history.
Don’t use rebase
on shared branches where others are likely to work. Your changes to your repository are going to cause problems to a lot of people when you push your rebased code to your remote repository.
If your project has multiple contributors, the safe thing to do is only use rebase
on your local repository, and not on public branches. Likewise, if pull requests form part of your code reviews, don’t use rebase
. Or at least, don’t use rebase
after creating the pull request. Other developers are likely to be looking at your commits, which means that those changes are on a public branch, even if they’re not on the master
branch.
The danger is that you are going to rebase
commits that have already been pushed to a remote repository, and other developers might have already based work on those commits. Your local rebase
will make those existing commits vanish. If you push those changes to the repository you’re not going to be popular.
Other contributors will have to go through a messy merge
to get their work pushed back to the repository. If you then pull their changes back to your local repository, you’re then faced with unpicking a mess of duplicated changes.
To Rebase, or Not to Rebase?
Rebase
might be outlawed in your project. There may be local, cultural objections. Some projects or organizations consider rebase
as a form of heresy, and an act of desecration. Some people believe the Git history should be an inviolable, permanent record of what has happened. So, rebase
might be off the table.
But, used locally, on private branches, rebase
is a useful tool.
Push after you’ve rebased, and restrict it to branches where you’re the only developer. Or at least, where all development has stopped, and no one else has based any other work off your branch’s commits.
Do that and you’ll avoid any issues.