Those who have eaten a chunk of wasabi thinking it was a chunk of avocado have learned the importance of distinguishing between two very similar things. Understanding the difference between Git’s merge and rebase commands may not be as essential to your physical well-being, but the point still stands.
git merge and git rebase offer the same service: incorporating commits from one Git branch into another. The key distinction lies in how this result is achieved.
Let’s find out how, shall we?
As Easy as ABC
We'll base our short discussion on the most meager “website” ever conceived. Initially, this website consists of a single page with the letters “ab” on it. That’s it.
Often in Git workflows, developers will create feature branches to work on new features in isolation. This gives them the freedom to make incremental commits for an in-progress feature without affecting the project’s master branch (more information about using feature branches can be found in Atlassian’s Git tutorials).
In our project’s Git repository, two branches currently exist: the usual master branch, and a feature branch that we created right after the initial commit. On the master branch, we have italicized the ‘a’, then bolded the ‘a’. On our feature branch, we have italicized the ‘b’, and then bolded the ‘b’.
Let’s figure out how merge and rebase differ by going through a couple of typical Git workflow situations with this project.
We believe our redesign of the letter ‘b’ is nothing short of a masterpiece, so we decide we want to bring our work back into the master branch, incorporating it into the actual project:
git checkout master
git merge feature
By merging feature into master, master obtains a new commit — a “merge commit”.
All by itself, the merge commit represents every change that has occurred on feature since it branched from master. Clean and simple.
The downside of merging? If used too liberally, merge commits can clutter up your Git logs, and make it much more difficult to understand the flow of your project’s history. For collaborative workflows in which other developers (and potentially even project managers/leads or QA) read your project’s Git logs for insight and context, readability is important.
To avoid this pitfall, try to use merges purposefully and sparingly. Avoid branching and merging when only making minor tweaks or trivial bug fixes. Use merge for cases where you want a set of commits to stand out.
Large refactors and major feature additions are good candidates for separate feature branches that can later be merged into master. As an added bonus, when merges are reserved for these major changes, the merge commits act as milestones that others can use to figure out when these major changes were incorporated into the project.
Let’s rewind, and pretend that we instead wanted to keep working on styling the letter ‘b’ — maybe change its size, font, color, etc. We need to get it just right — this change affects 50% of our codebase! But before we continue working on our feature branch, we decide we want to bring in the latest changes from master to keep things fresh.
Rather than merging master’s new commits into feature, we opt to rebase our feature branch onto master.
git checkout feature
git rebase master
At a high level, rebasing can be understood as “moving the base of a branch onto a different position”. Think of it like a redo — “I meant to start here.”
At a lower level, what rebase actually does is pluck commits from a branch one by one (chronologically) and re-attach them to a different commit. The point at which the branch…branched has now changed.
You might see from the diagrams above why we would choose to rebase instead of merge in this situation. Unlike with merging, rebase does not create an extra commit. This is ideal for our situation, since all we are currently trying to do is keep our feature branch up-to-date with any new commits from master. This is definitely not a meaningful event we want to preserve in our project’s history.
Although the changes on the newly rebased feature branch are identical to what they were before, it is good to note that, from Git’s perspective, these are new commits with new SHA’s (the commits’ identifiers).
But even more importantly, realize that the feature branch’s history has been completely rewritten. That sounds a bit heavy, doesn’t it? You might wonder if there are implications to this you should worry about.
Although unimportant when you are the only person working on a particular branch, reckless rebasing can be a major issue when collaborating with others. In short, the potential arises where others have copies of the branch checked out (with unaltered history) which conflict with your altered copy.
Pretend that another developer named Mark is working on the feature branch alongside you. He checked out the feature branch at its second commit — when the ‘b’ was italicized. While we rebased our version of the feature branch onto master, Mark innocently decided to add another commit on his own version, adding the letter ‘c’. Here is what the scenario would look like:
Even with our dead-simple project, it would take a little effort and brainpower to figure out how to resolve the discrepancies. Imagine trying to resolve this kind of conflict on a real-life, larger scale project. Not a lot of fun.
You can read more about the proper way to rebase here: The Golden Rule of Rebasing. The one-line summary: don’t rebase a branch unless you are the only one who uses it. Or you enjoy chaos.
Time to Make Like a Tree and Leaf
In summary, when looking to incorporate changes from one Git branch into another:
- Use merge in cases where you want a set of commits to be clearly grouped together in history
- Use rebase when you want to keep a linear commit history
- DON’T use rebase on a public/shared branch
That’s all I got. If anything in the article was unclear, or you have any questions, comments, or suggestions, please feel free to leave them in the responses below! Thanks for your time!