Article summary
One of the most powerful features of Git is its ability to create “branches” of a codebase that can be developed in parallel. This lets multiple team members contribute to different features simultaneously without stepping on each other’s toes. But sometimes, a feature branch gets out-of-date with its parent branch. There are two popular strategies for reconciling this: merging and rebasing. In this post, we’ll weigh the pros and cons of both options and help inform your decision to use one or the other.
The Scenario
Let’s say you have two branches, main
and feature
. The feature
branch was originally based off main
, and new changes have appeared on main
since then.
Git Merge
Merging is a strategy that allows you to incorporate the main
branch’s changes into your feature
branch via a “merge commit.” While on the feature
branch, you can perform this by running the following:
git merge main
This results in the following branch structure, with the *
representing a merge commit:
Merging is a simple, easy way to pull in the latest changes from a base branch. Conflicts can be handled all at once, and a merge can easily be reverted if you make a mistake. This makes merging a great option while you’re first getting started with Git.
However, merging in the main
branch every time it changes can muddy up your feature
branch with extraneous merge commits and make it hard for other contributors to follow the history of the branch. Many Git tools have workarounds for this. The Git CLI, for example, allows you to use the --no-merges
flag to filter out merge commits in your git log
.
Merging allows easy co-ownership of branches. It maintains your commit history as it happened — no rewriting or reordering of commits. This makes it easy for multiple teammates to work on the same branch, which, as we’ll see, isn’t always the case with rebasing.
Git Rebase
Rebasing is a strategy that re-applies all of the changes from your feature
branch on top of the head of the main
branch. Instead of creating a merge commit, rebasing will rewrite the entire history of your current branch, as if the work had been re-done on top of a different head.
git rebase main
This results in the following branch structure. Notice how the commits from the feature
branch have moved to the head of main
:
This can drastically improve readability by making your branch’s history linear — one step after another, with no extraneous merge commits.
Rebasing also allows you to reorder and redistribute your changes to clean up the history of a branch. For example, you can squash together “WIP” commits and synthesize them into more meaningful changes. Plus, if you make an embarrassing mistake on a branch (say, exposing a secret key in the source code), rebasing allows you to remove any traces of that mistake. (In contrast, with a merge, the mistake would still exist in the Git history.)
Rebasing, when done right, can make your branches easier to read and review. But it’s a double-edged sword: rebasing is a destructive action. If you mess up a rebase, there’s no going back (at least not without some shenanigans). Because rebasing rewrites the commit history, your branch can get out of sync with other people working on the branch, causing confusing errors when trying to synchronize changes.
Note that to push a rebased branch to a remote repository (e.g. GitHub), you have to force-push. I like to use --force-with-lease
to make sure I’m not overriding any changes that have happened upstream.
Git Merge or Rebase?
Advocates of Git Merge argue that merging prevents the potentially-catastrophic consequences of a nasty rebase. Because merging is a simpler, less error-prone way to achieve the same goal as a rebase, they argue that it should be preferred. Maintaining the history of changes can also help future developers step into the shoes of past developers, building empathy and giving insight into the choices and tradeoffs made at the time the code was written.
On the other hand, advocates of Git Rebase emphasize the importance of a clean, readable history. They argue that keeping your history pristine not only eases the code review process but also helps prevent future developers from repeating outdated patterns. They claim that developers should know their tools like a chef knows his knives. Reaching for the “easier” option gets the job done, but it doesn’t deepen your understanding of how Git works as a whole.
Both sides have strong arguments, so I don’t tend to lean toward one or the other. At the end of the day, merging and rebasing are two different means to the same end: getting a branch up-to-date with its parent. Instead, I use this rule of thumb, and I’d encourage you to follow it as well: if you’re working on a branch by yourself, rebase away! If you’re working on a branch with other people, prefer merging instead.
Do you prefer merging, rebasing, or some mix of both?