Read Anthropic’s case study about Graphite Reviewer

Git Merge vs. Rebase: The Differences and Benefits of Both

Greg Foster
Greg Foster
Graphite software engineer


Note

This guide explains this concept in vanilla Git. For Graphite documentation, see our CLI docs.


Git Merge vs Rebase

One of the most important features of modern development is version control. We've all heard stories of the people who develop in production, who push an update at 4:30 pm on a Friday and break everything with no easy way to revert the changes, and even lose code that costs dozens of hours of development time to restore.

While a variety of source control systems exist, including Mercurial, SVN, and proprietary solutions like Google's Piper, Git and GitHub have emerged as the de facto standard for version control in the software development industry.

Of course, knowing what GitHub is and even how to use it doesn't mean you have a full understanding of the options available to you. For example, should you use Rebase or Merge when you commit? Read on, and this Git tutorial will help you choose.

Before we get started, there are two important terms that will come up throughout this post that you should make sure you know.

  • Commit. A commit is basically a snapshot of the files in a codebase. Say you're building an app, and you have three different files you change to add a new feature. When you save these files to GitHub using Git, you are committing these files to Git in a single commit. Each commit is assigned a hash; if you need to go back to an earlier commit, it's easy to find that ID and restore it. More changes down the road, add new commits.

  • Branch. A branch is basically a pointer to some commit. Say you're building an app, and you have a current version that works fine. You want to add a new feature, but since it's a complex feature, you can't develop, add, and test it all at once. In order to not disrupt the main codebase, you create a new branch, which can point to new commits where you can edit and tinker with the code without needing to worry about your changes breaking something in the main app.

When you finish building your feature and want to add it to the main app, you perform an action to roll these changes into the “main” branch of code, effectively closing the feature branch and locking in the new feature. This is usually referred to as a merge, but there are two different strategies you can use to implement it: the Merge and the Rebase.

Terms to Know

The inverse is also the same operation; when the master branch changes before you're done implementing your feature, and you need these changes to be implemented in your branch so you can work on the most up-to-date code in case it's relevant, you need to merge the master into your branch for the update.

That brings us to the main question: what is the difference between the merge command and a rebase? They solve the same problem but in different ways and can introduce problems of their own, especially if you aren't careful with how you use them. So, let's talk about it.

First, let's start with a merge. Merging is a method for integrating two code branches within a Git repository to preserve the history of both branches. This preserves the entire development history, but it does result in a more complex and non-linear history that might be hard to follow. While it's known to be a "non-destructive" method, it's important to note this process does not completely remove the potential for conflicts that may complicate a merging operation.

In order to illustrate how merging works, let's create a hypothetical. You have, of course, your main branch of code, the master code. This is the canonical codebase from which all other code is split off to work before features are rolled back into and committed to it, which creates a new version of the master code.

You want to work on a feature, so you split off the master code into a new branch and call it FeatureBranch. You spend some time working on the feature in FeatureBranch, committing to your branch as necessary to save your work at important steps along the way.

Other developers on your team members are working on the same master code at the same time, probably working on other features and projects. So, what happens if another developer pushes a commit to the main master code? Well, it depends on whether or not your code relies on code that is now changed. If it doesn't, it doesn't really matter. If it does, though, you're left in a difficult situation.

How Merging Works

A merge is one way to solve this issue. During a merge, you bring the latest updates from the master branch (or any other branch) into your current working branch. This process ensures your branch reflects the most recent changes made in the master branch, which allows you to continue your work with an updated codebase.

This creates a "diamond shape" in the commit history for your feature. If someone is following the history of the main branch of the project, they can see that commit F came after commit E, which is followed by commit D, and so on.

If they follow the Git history of your feature, though, they see that commit Br2 came after Br1 which came after C - but they also see that F was merged into Br3, so Br3 technically has two parent commits; Br2 and F.

It's more thorough but less clean and clear about where bits of code are coming from and creates a non-linear map of the project. Some developers hate this and prefer a simpler project history, which is why Rebase was invented.

Rebasing is sort of like rewriting history on your feature branch.

When you branch off the main branch, whether into a local branch or a remote branch, your base code is the same as the main code. As you make changes to your branch, you still have a straight line of development from the main code base to the current state of your feature branch.

If the main branch changes, though, that straight line is now a fork. You can't merge back in cleanly because there could be conflicts between changes you made to files and changes someone else made to these same files.

How Rebasing Works

Merging solves the issue by pushing the main branch's changes into your branch so you can adapt and keep moving forward.

Rebasing does the same thing, but the way it records the change is different. Instead of creating a diamond in the project history, a rebase changes what the project sees as the base from which you were originally forked.

Rebasing a branch essentially means presenting it as if it originated from a more recent commit, instead of acknowledging its actual starting point in the project's history. This can be problematic, especially when the changes have already been shared with others through a communal repository, as it changes the shared history and can lead to conflicts.

Merging is like a team huddle in coding. Imagine you've been working on your piece of the project, and now it's time to show it to the rest of your team. You bring your work to the table, and so does everyone else. Then, together, you figure out how to weave all the different parts into one strong and coherent whole.

See, when you merge in Git, you take the updated main section of the project and add in your own special part, or you mix your finished work back into the main section for everyone to use. It's like a group project where everyone's individual work gets combined, so the main project always has the newest updates from everyone.

But don't worry; it's not **all **about piling things on; merging also keeps track of who did what. You can look back and see the history of both the main project and your own work, just like a scrapbook. That said, this scrapbook can get pretty complicated, especially when a lot of people add to it commonly - it can be difficult to read.

Sometimes, when everyone brings their work to the huddle, things clash. Two people might have different ideas on how something should look. That's a merge conflict in the coding world. But just like in any good team, you sort it out, find the best path forward, and keep your project clean and running smoothly. After all, a well-kept project is one that works better and is easier for everyone to know and use.

Pros and Cons

It has a few pros and cons, though, which are worth understanding rather than basically accepting as the only way things can be done.

  • **Pro: **The complete history of the project is retained and accurate. While the non-linear diamond-shaped version of a feature's history may be complex and difficult to follow, it's accurate and can be analyzed, traced, and usually reviewed to resolve conflicts, look at issues or for meta-analysis of a project down the line. Critically, this maintains the context of changes made to the feature branch, which can be lost with a rebase.

  • **Pro: **Merging is a non-destructive way of combining code bases. Nothing about the history of either the branch or the main code is altered, and a new merged commit is basically made the new canon. There's no rewriting of history as there is with rebasing.

  • **Pro: **Merging is the way most developers learn Git, and it's the one that makes the most logical sense to most people. It might not be the cleanest or most orderly option, especially when reviewing the history of a project down the line, but it's simple and easy to know in concept.

So what about the downsides?

  • **Con: **Some developers don't find merge to be very user-friendly. This often comes down to how well you know conceptually what is happening in a merge versus a rebase, how complex the project is as a whole, and how big-picture your view of the project is. When different feature teams are siloed and isolated, or in projects with very large contributor bases, merging can be almost impossible to fully grasp.

  • **Con: **The commit logs kept by Git when merging into play can be very messy. A detailed history of commits can be handy to have, but these records have to be managed carefully to prevent disorganization. This tends to become a bigger issue as the project's size and activity level increases, as maintenance and organization becomes more of a challenge.

Next, let's chat about the pros and cons of Git Rebase.

A rebase is often considered a cleaner and more linear version of a project's history and allows for one "canon" history to be written, even if the actual reality was a lot messier behind the scenes.

Rebase

The pros are quite beneficial, but the cons can be highly harmful, especially in some situations.

  • **Pro: **The history of a project becomes very streamlined and linear, with none of the branching diamond-shaped merging that comes with Merge commits. With complex and multifaceted projects, this can be a huge benefit to readability, even if it's not actually strictly accurate.

  • **Pro: **Intermediate commits along the development of a project are basically lumped into one, which makes the management of an overall project easier. DevOps doesn't have to worry about micro-milestones and excessive commits when they're cleaned up and rolled into one.

  • **Pro: **Logging is made immensely easier. Since the non-linearity of merging is removed, you have a simple version history of commits that track changes to the code and features added to the main branch. The logging is streamlined and doesn't require any of the cleanup characteristics of complex merged projects.

All of that can be good, but there are also reasons why it might not be.

  • **Con: **Rebasing modifies the project's historical context, which can potentially obscure the original timeline of developments.

  • **Con: **When there are conflicts in the files that you rebase, you need to resolve them in the order of creation through commits. Sadly, if you need to rebase through conflicts more than once, there's a high chance you're going to have to fix the same problem several times, wasting time and effort.

  • **Con: **Since you're rolling commits into each other and rewriting history, it becomes a lot harder to find what the context was surrounding a given commit or merge. While the nice, linear history is pleasant to look at, it's basically a pleasant fiction.

The pros can be quite beneficial but you have to be aware of the record-keeping and conflict-resolving limitations.

Many developers ask if it is better to merge or rebase when you commit.

Merging vs Rebasing Illustration

**The answer is neither. **Rather, they both have their uses, and it depends on the goals of the project, the difficulty and number of people working on the project, and more.

In the space of version control with Git, it's really beneficial to master the different branching strategies and choose when to merge or rebase to keep your projects on the right track. If you're starting out and looking for a Git guide, or if you're an experienced dev aiming to up your team's game, maximizing Git's capabilities is important.

Understanding the difference between git pull and git fetch is important; while git fetch keeps your remote-tracking branches updated without changing your current work, git pull does both fetch updates and merges them into your branch, which might change your work setup.

At the heart of managing a repository well, there's effective git branching. With git checkout to switch branches and git branch to manage them, developers can focus on separate features without messing with the main code. This separation is important for adding new features or fixing bugs.

Facing merge conflicts when branches go their separate ways? It's really important to sort them out quickly to keep your workflow smooth. Tools like git merge and interactive rebase are there to help. Personally, I find interactive rebase valuable for tidying up a commit history before merging features back into the main branch.

For beginners, it's important to get familiar with Git commands like git log, for a full history of commits, git push to bring your local commits to the remote repository, and git fetch to sync your work with remote updates. Pull requests also play a big part in Git and let you review code and collaborate before finalizing changes. I recommend that beginners use these commands often in the command line to stay updated.

When you hit a merge conflict, it's important to know the source and target branches for a clean resolution. Sometimes, you might need to force push, but be careful - it can overwrite remote history.

A strong understanding of branching can really improve teamwork and project management, whether you're coding solo or as part of a larger dev team. That includes merging, rebasing, branch history, and a solid strategy.

Git is a powerhouse for version control, and it's important for any project. But let's face it, it can be a bit awkward and definitely shows its age sometimes. Ever thought about how to supercharge your development workflow while sticking with Git and GitHub, along with all the collaboration tools you're used to?

Face it, it can be a bit awkward, and it definitely shows its age sometimes. Ever thought about how to supercharge your development workflow while sticking with Git and GitHub, along with all the collaboration tools you're used to?

Graphite is our answer. It's like the ultimate Swiss army knife for Git, with streamlined PRs, elimination of tedious log sanitizing and merge management, and more.

Graphite Screenshot

It's easy to adopt, powerful to use, and works individually or as a tool for the whole team. Check it out! You'll be happy you did.

Graphite
Git stacked on GitHub

Stacked pull requests are easier to read, easier to write, and easier to manage.
Teams that stack ship better software, faster.

Or install our CLI.
Product Screenshot 1
Product Screenshot 2