loading...
Cover image for There Is No "Right" Way: Git Rebase vs Merge

There Is No "Right" Way: Git Rebase vs Merge

molly_struve profile image Molly Struve (she/her) ・3 min read

In honor of my new license plate, I decided I wanted to do a quick, little post on rebase vs merge, how each one works, and how I choose to use each one. Let's first look at how each one works.

How Rebase and Merge Work

To start we have two branches. We have our master branch and we have a Feature 2 branch that we just cut from master. Both are shown below. Since we just cut our Feature 2 branch it is the exact same as master, nothing has changed yet.

feature and master branches to start

After a couple of days, our Feature 2 branch has some new commits on it with the new feature work that has been done.

feature branch after 2 days

During that same couple of days, the master branch also had a couple of new commits added from another developer who is also working on the project.

master branch after 2 days

Now we are ready to test our Feature 2 branch but before we test it we want to make sure it is updated with all of the new code that is now in master. In order to update our Feature 2 branch with the new changes in master we have two options, we can merge or we can rebase.

Merging

The merge command will let us take all of the development changes present on the master branch and integrate them into the Feature 2 branch with a single merge commit.

merge master

That single merge commit encompasses all of the changes from the master branch. One downside to merging is that if your master branch is very active it can pollute your feature branch’s history quite a bit. If you prefer to not have merge commits in your feature branch history then you might want to consider rebasing.

Rebasing

We can use the rebase command to integrate changes from the master branch into our Feature 2 branch by rewriting the commit history in order to produce a straight, linear succession of commits. Rebasing moves the entire Feature 2 branch to begin on the tip of the master branch.

rebase master

As you can see there are no extra commits and everything is lined up as if we had just added the Feature 2 commits right on top of the new master branch.

My Rebase vs Merge Strategy

When it comes to rebasing vs merging, I use both in different scenarios. When I need to update a feature branch I am working on with master I will ALWAYS rebase. I choose to do this because I like my branch history clean. If all the commits are new code it is easier for me to read and follow. I use merging when it comes time to put my branch changes into master. I like to do this because then I get a single commit that is easy to find with all my branch changes in it.

In my experience, merging when incorporating a feature branch into a master branch is the most common practice when you are working on a repo with a team. How you choose to handle updating your own personal feature branches is usually left up to you and leads me to my next point.

There Is No "Right" Way

Rebasing vs Merging is one of those age-old debates that exist in the dev community much like tabs vs spaces. Also, like tabs vs spaces, when it comes to rebasing vs merging there is no right way to do things. Some people are adamant that you must always merge. Others believe the only correct way to do things is by rebasing. Guess what, neither are right or wrong! How you choose to use rebase vs merge is really up to you and your workflow.

The same concept applies to MANY common dev debates like how many characters a single line should be, how many lines a method should have, etc. Often when you are learning something new all you want to know is the "right" way to do things. You are constantly looking for someone to tell you what to do. Unfortunately, it is not always that easy. When you are starting out, don't look for the right way, look for the way that works best for you. Everyone has their own style and way they work most efficiently. Look around you, soak in what other devs are doing, but in the end decide for yourself what works best.

What other "dev debates" can you think of where both ways work and it's simply personal preference as to which one you choose?

Posted on by:

molly_struve profile

Molly Struve (she/her)

@molly_struve

International Speaker πŸ—£ Runner πŸƒβ€β™€οΈ Always Ambitious. Never Satisfied. I ride πŸ¦„'s IRL

Discussion

markdown guide
 

Ah, the venomous arguments about trivial things...

On this particular topic, my preference is not to rewrite public history. Rebasing a feature branch that's already on the remote seems wrong to me, so at that point I will always merge master into it. However locally I will rebase most of the time, for all the reasons you outlined here.

Which leads into my example - to squash or not to squash. I've worked with people that think every merge should have a single commit. And others that are just as adamant that you should never rewrite history and every commit should be left the way it was created.

Similar to my merge/rebase preference, I will cleanup my messy local commits by squashing them before pushing. Often I won't squash though because I think the commits are separate things and if I need to revert later on I can be more selective. But what I won't do is squash corrective commits with original commits. If I get feedback on a PR and then I incorporate it I want that history to be preserved. Bizarrely to me, there are some of the 'there shall only be one' committers' who would re-write their branch commits so that changes made after feedback would be incorporated into the same commit as their original changes. They say it's cleaner - to me it's like lying about your work.

 

The real question is what do you want to get out of your history?

To me history is the why. I like to use the example of undo trees, seen in emacs and vim, these could be view as in memory commits. They have branching and timestamps, but limited in scope to a single file.

The reality is, this isn't the history people keep. They work and craft out a spot they believe is complete. Or they adhere to someone commit twice a day rule. Either way the history is not real.

There are specific tools which make use of history. bisect which searches for a commit. blame which describes a file based on its makeup of commits. revert which is the 'undo' of git, cherry-pick the copy/paste of git. I want to build out a history which maximize the usefulness of these tools.

Git also provides the tools to help with that, staging area, commit --fixup, rebase -i, and more. Fixup is great for code reviews while allowing for quick cleanup before merging.

Yes it doesn't matter, unless your goals line up with mine.

 

Regarding squashed changes for me the answer is simple: on larger projects squash, on smaller projects don't. Reason being is on smaller projects having the complete history right there is useful. On larger ones you'll be forced to scroll through dozens, hundreds or even thousands of commits to find even a relatively recent change. No-one wants to do that.

 

Regarding venomous arguments over trivia seems to me there are a few reasons for this.

The first is that sometimes seeming trivia really does matter.

The second is that some devs lack empathy and can't understand other's preferences.

Following on from these, to non-techies the one can look very much like the other.

Bad devs will often argue personal points on genuine trivia because it makes things nicer for them personally, and since there are likely few real world consequences they don't have to worry about being wrong. Or they just can't tell the difference...

Good and great devs will often still argue trivia for the reason outlined: non-techies and bad devs often can't tell the difference and being seen to be knowledgeable in general can establish them in a position where they can improve the project as a whole, rather than deferring to bad or mediocre devs who would create an inferior product.

 

Good call on squash or not to squash argument! That always gets our devs going at Kenna lol

 

The real argument is about what history is and how to preserve it. It's NOT the minutiae; It's the narrative of what the commit does when applied. So rebase -i to craft that narrative! Or git commit --amends. Remember that git is collaboration tool too.

Keeping a history littered with 'add new comment'-like goo should be squashed and never be in Master. Also, It's almost pointless to merge a single squashed commit to master.

So, If you're pro Master branch merge commits, it best to have a crystal clear rational/strategy as to why you need to keep and share that commit history as the product dev proceeds. Otherwise it's goo.

I'm for a linear history with rare merge commits.

 

Hi Molly,

I feel excited after reading. For your opinion that rebase should be used when "feature branch needs to be updated to master" which we can make the history clean. Then there is a pathetic pain that it's really difficult for code reviewing on pull request times -- the request shall contains redundant commits from master!

Let's assume the feature branch has 3 devs working on it. My way is to assign one to hold the role for keeping that branch up to date which means he/she will fetch new commits + rebase onto master (resolving conflicts if there is) and then notify other 2 devs to stop pushing new commits in case of lost commits. Means everyone have to wait for the "forced push". In the end, the feature branch has code as expected and everyone can push, pull, fetch normally. The problem is it's painful to postpone every members in branch for just an updating. And what if the branch has a lot of participants like over 10 people, how can we solve this without "forced push"?

 

In my initial years doing web development, something weird that used to happen to the team was that sometimes when doing git merge changes would go missing.

This caused the affected pals to be wary of git even to the point of thinking not to use it.

It wasn't until last year(Q4) that I really understood how rebasing works and why that situation happened.

So, I really agree with you that *neither rebase or merge are the right way but rebasing is the way to prevent merges that "remove" code.

How come?

Well, if two devs modify same file, same lines and Dev A pushes to master and then Dev B pulls, if it doesn't do a merge conflict git will remove Dev B changes and place Dev A ones.

I don't really recall the exact conditions for this to happen but it's something like this.

This is when rebasing comes in hand because it kind of respects the commit history and moves rebased commits to the beginning/tip.

Currently, I mostly use git merge but when I feel that situation could happen, I go with git rebase.

πŸ‘πŸΎπŸ‘πŸΎ

 

The app I'm working on has rebasing ENFORCED!

This is achieved by blocking pushes on a subtask branch if it is behind it's feature branch. (Development is done on subbranches, testing is done on feature branches, and code review happens in the PR between the two.) Git remote actually responds with the exact commands you need to run in order to get up to speed.

This works pretty well because everyone does it, and our history is just so pretty πŸ™„.

My biggest beef with this is that it messes with my code review process: The way I work is I review all the code, and if I find issues I comment on them, and decline the PR. The developer then makes the necessary changes, pushes new commits and reopens the PR. At this point I don't want to review all the code. I want to review the new commits only. But since it was rebased, it's hard to find the new changes (and don't get me started on squashed commits).

The integrity of the hashes are also important in this case. It prevents people from perhaps using shortcuts that would of otherwise not been approved in the standard process.

Another issue is that when conflicts are resolved by a different developer, the commit still has the original developers name on it, Which just confuses them.

So as long as no one else involved in a branch, rebase and squash away (YOLO), but please don't enforce it.

 

When you are starting out, don't look for the right way, look for the way that works best for you.

This.

So, what I get from this article, it makes sense to rebase "downstream" and merge "upstream". I like that. I will be like Molly.

And if it doesn't work, my pragmatic nature will overcome this with ease.

Very straight forward article by the way. Thank you for that.

 

Great explanation! I was actually confused reading someone else's blog on this but yours explains it perfectly! It also explains how I've never used rebase, and why I've always gotten by with git merge haha. I will take on your suggestion to use rebase when merging master back into a feature branch.

 

Can't say much about rebase -- Rebase is evil. Ok, maybe not evil, maybe it's necessary given the requirements git needs to support (huge Linux repos). But history should never be forgotten, and that's what rebase does. I've been working with Fossil and prefer it.

No flames, not trying to convert anyone -- here's a comparison of the two:

fossil-scm.org/home/doc/trunk/www/...

And an article from yesterday:

theregister.co.uk/2019/12/03/githu...

Again, not trying to convert, just showing another option.

/s.

 

Very well written! Your strategy is same as the mine; also I never rebase a remote on my local branch

 

Thank you! I know many others have done posts on rebasing vs merging but hopefully sharing my perspective and/or explanation will help someone understand it better!

 

You can trust me: among all the merge vs rebase post, your is the only comprehensible!

 

Every time I tried rebase, it screwed up my repo

So since then I stayed on merge πŸ˜…

 

Thanks for sharing this way of your working ;) grabbed many things.

 

I think I finally understood rebase. 😳

 

To my defense I must add, that I am working with only for a few months now, and never had the usecase for something like rebase.

 

Thank you so much for this super clear explanation! I’ve seen so many posts attempt to do this and fail. Also awesome custom license plate!

 
 
 
 

It seems to me that saying "there is no 'right' way" is almost always a cop-out, especially when it's followed by an argument for your preferred "'right' way".

like tabs vs spaces, when it comes to rebasing vs merging there is no right way to do things

This is clearly incorrect in both cases. The right way to do things is the way your team prefers.