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.
Each circle in the sequence represents a commit. An arrow goes from a parent commit to a child commit (from a previous commit to the next commit).
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 main
branch.
You now need to bring the new changes in the main
branch into your cool-feature
branch.
Git Merge
If you want to merge the changes into your branch, you would run the command git merge main
while still on your cool-feature
branch.
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 main
branch.
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
main
branch. - The latest commit on the
cool-feature
branch. - The latest commit that is common in both branches.
Git Rebase
To perform a rebase, you would run the command git rebase main
while on your cool-feature
branch.
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 main
branch.
Which One Should You Use?
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.
Top comments (0)