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.
Let’s say you have two branches,
feature branch was originally based off
main, and new changes have appeared on
main since then.
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
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.
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
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?