Every developer from the Git community has either faced the
git merge vs
git rebase dilemma or has been a part of this debate. Any git beginner is advised to stay away from
git rebase as it is a destructive process, but I find it as a great tool to simplify our development process within a team if used carefully.
In this post, I’ll compare
git rebase with the related git merge command and identify all of the potential situations where we can use rebasing in the typical Git workflow.
Rebase and merge, both solve the same problem - both these commands are designed to integrate changes from one branch into another branch. They differ in the manner in which they achieve this.
To understand the working of these commands, let assume that you started working on a new project. You head to the repository, fork it, set up your local workspace, and start working in your feature branch. Meanwhile, a colleague of yours makes a commit in the master branch of the repository. And your history looks something like this:
To get these new commits into your feature branch, you have two options: merging or rebasing.
Suppose you use the following commands and merge your changes:
git fetch git merge feature-branch master
This will also create a new merge commit in your log. Merging is nice because it’s a non-destructive operation. The existing branches are not changed in any way and the history of your branch is preserved.
But, every time you sync your fork with the upstream you will be creating an extra merge commit to incorporate the changes. If your master is very active, your feature branch history will be loaded with the merge commits. This might give hard time to someone who goes through your branch/project history.
As an alternative to
git merge you can do:
git fetch git checkout feature-branch git rebase origin/master
This moves the entire feature branch to begin on the tip of the master branch by combining all the changes of the master branch into a single “patch” and then integrate it onto the feature branch. But, instead of using a merge commit, rebasing re-writes the project history by creating brand new commits for each commit in the master branch. In the process, unwanted history is eliminated. Hence, your tree will look like something like this:
And this is the major benefit of rebasing - A clear and linear project history. It has eliminated the unnecessary merge commits required by git merge. And now that you have a linear project history, you can follow the tip of the feature all the way to the beginning of the project without any forks. This makes it easier to navigate your project with commands like git log or git bisect. The only downside of using this is that now we will never know when was the upstream changes incorporated in our feature branch.
But what if I told you that you can completely alter the project history and make it look as you want it? Powerful isn't it?
Interactive rebasing will give you complete command over your commit history as you can alter commits as they are moved to the new branch. Usually, we use it to clean up a messy history before merging a feature branch into master. For example combining previous "work in progress" commits, which have been completed.
git checkout feature git rebase -i master
This will open the editor with the list of your commits, and you can choose to pick, squash the commits to make the history look as you want.
If you are working on your branch individually, rebasing makes a lot of sense. You can use rebasing to streamline a complex history, you can also change the commit history by interactive rebase. You can remove undesired commits, squash two or more commits into one, or edit the commit messages before creating your Pull Request. This will also simplify the work of the reviewer as he/she will have to go through a clean commit history.
If you want to see the history completely the same as it happened and avoid the risk of re-writing public commits, you should use git merge.
The most important thing to learn about rebasing is when not to do it. The golden rule of git rebase is to never use it on public branches, the common branch which all developers will use as it will create inconsistent repositories which can result in your fork diverging from the main repository.
For example, let's say we rebased our master onto our feature branch using
git checkout master git rebase origin/feature-branch
This action will move all the commits in your feature branch first, then the commits of the master will be put on its tip. The problem is that this only happened in your repository. All of the other developers are still working with the original master. Since rebasing results in brand new commits, Git will think that your master branch’s history has diverged from everybody else’s.
The only fix now is to merge the two master branches together, but it will result in an extra commit with both sets of changes, the original master commits, and your rebased feature branch too...
So, before you run git rebase, always ask yourself, “Is anyone else looking at this branch?” If the answer is yes, bad news, stick to the normal git merge process, otherwise, feel free to rewrite history as you like.