I personally struggled to understand the difference between merging and rebasing in Git, especially since they both did what I wanted in the end (bring changes in another branch into my current branch), until I visualized what exactly happens in both processes.
To compare the two, let us assume that the
main branch of your repository is where all the code is kept up-to-date.
To work on a new feature, you check out a new branch
cool-feature and make a few commits.
In the meantime, some of your team members finished a feature they were working on and merged their commits to the
You now need to bring the new changes in the
main branch into your
If you want to merge the changes into your branch, you would run the command
git merge main while still on your
This will replay the changes contained in commits on the
main branch since the first common commit between the two branches. A new merge commit will be created in your
cool-feature branch that contains all the latest changes from the
If both branches have changes to the same lines, then Git cannot determine which of the two changes to keep (the one in the
main branch or the one in the
cool-feature branch). This results in what Git calls a conflict that the user has to resolve. After making necessary changes in the file that has a conflict (i.e. deciding whether to keep the changes from the
main branch, the
cool-feature branch or a mix of both) the user can commit these changes as a part of the merge commit created.
The merge commit has two parent commits: the latest commits on both the branches.
The algorithm Git uses to merge changes this way is called the 3-way merge algorithm because it uses three commits to generate the merge commit:
- The latest commit on the
- The latest commit on the
- The latest commit that is common in both branches.
To perform a rebase, you would run the command
git rebase main while on your
This will replay each commit in your
cool-feature branch on top of the latest state of the
main branch. For each commit in your
cool-feature branch, a new commit will be created which contains the same changes as the initial commit.
In case there are conflicts when applying each commit, Git will stop rebasing and notify you so that you can resolve them. The changes you make to resolve the conflicts will be added to the new commit.
Git also gives you the option of performing an interactive rebase where you can decide what to do with each commit that will be reapplied to the
The first question I asked myself after understanding the difference between the two was: which one should I use?
Not surprisingly, like most things in software engineering, the answer turned out to be it depends. Merging and rebasing give you two completely different views of your repository's commit history.
The downside of using merge is the creation of merge commits that can clutter the commit history of your repository. In a project where a lot of changes are actively being added to the main branch, your feature branch will have a new merge commit in it every time you merge it with the main branch. This can make it difficult to clearly see the actual commits you are making to implement the feature.
On the other hand, using rebase is not as straight forward as using merge. Since rebasing re-writes history (by dropping old commits and creating new ones), rebasing a branch that you have previously pushed (e.g. to GitHub) and others have started using can cause problems. You will now have a different commit history after the rebase from what others accessing the same branch have. If they do not pull this new rewritten history, they will be working on an outdated copy of the branch. This might become a problem when they try to push their changes in a branch with an outdated history.
Personally, if I am working on a feature branch that I have not yet pushed upstream (and as a result no one would have checked it out), I use rebase to avoid the extra merge commit. However, if the branch I am working on has already been made public, I avoid rewriting history with a rebase and live with the extra merge commit.