git merge vs git rebase: avoiding rebase hell
Learning Git can be a pain in the ass sometimes (OFTEN). One feature that has continually caused me to pull hair while I was climbing the learning curve is git-rebase. Rebase is a very useful tool, but also gives you more than enough rope to hang yourself with. Several questions need to be answered to fully understand a rebase:
1. How exactly does rebase differ from merge?
2. When/Why should you use it instead of a merge?
3. When should you absolutely not use it?
Let’s begin…
Rebase vs Merge
So first let’s talk about how Git handles a standard merge. A very typical scenario is where you would have one instance of a given repository living on some remote server somewhere acting as the home of your authoritative “master” branch instance. In this situation this is where all code ends up prior to deployment. This could be hosted on github or just a $200 linux box sitting under someone’s desk. Either way let’s consider this scenario:
So each black bar represents all of the commits that each instance has in common. In other words we see that our local branch, “master”, and the remote branch that it is tracking, “origin:master”, are both perfectly in sync.
So now the work day has begun and I commit some changes to my local branch instance (red). During this time another developer commits changes to his local branch instance and pushes them up to origin:master (blue). Now the two repositories look like this:
Not knowing that another developer has pushed new changes up to origin:master, I attempt a push myself and am promptly rejected. Realizing that I am not up to date with origin:master I execute the command “git pull origin master”. Git-pull executes a fetch and then a merge all in one command. The result being:
The change that lived on origin but not on my local repository is now merged in. Note the green “merge commit” placed at the top of my hypothetical commit stack. This commit will contain the result of any merge conflicts that you had to resolve. In the case there were no merge problems it is just an empty commit.
Rebase works differently. Given the previous scenario, when we do a pull –rebase (which calls fetch and then rebase instead of merge) it actually un-commits all of your local commits that are not already in origin:master (or whatever branch you are rebasing off of). After it unwinds all your hard work it then stuffs the new commits from the remote branch onto the stack and then plays your changes back on top of them - literally creating a new “base” for which to put your local commits on top of.
If conflicts are found while attempting to play back your changes it throws you into an unnamed branch and gives you a chance to merge everything together. After correcting the conflict(s) you would type “git rebase –continue” as directed by the conflict message. Now we get to the fundamental difference between a merge and a rebase: a rebase ALWAYS gets rid of all your original commits and creates brand new one’s that get placed on top. To say it another way - the original local (red colored) commit is GONE and, as illustrated below, is replaced with a new commit with an entirely different ID (green colored).
If we had two local commits prior to the rebase then we would end up with two brand spanking new commits after a successful rebase - both with new IDs. As far as git is concerned these are new commits…
You may have seen this before:
First, rewinding head to replay your work on top of it…
Fast-forwarded master to fde4b2d5cda60905fcf5973bc17cd051ec2c336d.
It means this quite literally.
When and why should we use Rebase instead of Merge?
Technically speaking you can fully use git without ever using rebase. Merge is perfectly fine for managing your code. When you merge, Git creates the extra commit on top of the stack that we talked about earlier. Git does this in order to track the point at which two diverging repositories have come back together into a common ancestor. This can be quite helpful to be able to, at a glance, tell when and where a parallel branch has been folded back into the master branch. On the other hand, if everyone on a given development team is working from one branch most of the time (say master) then the log can get quite noisy with all of the “merge branch blah” commits from your day to day “git pull origin master” workflow. Using “git pull –rebase origin master” will avoid those superfluous commit messages by pulling and then performing a rebase rather than a merge. This is handy for keeping the appearance of a linear commit history and makes merge commit messages (from other branches) more meaningful because they actually do represent some branch being folded into master.
When should you NEVER use Rebase?
Never, ever, ever do what I will refer to as “lateral rebasing”. What this means is that you should only rebase branches that are authoritative and for which the history will not retroactively change. By “retroactively change” I mean that the branch will only receive, never push them, and commits that are received will always be stacked on top of the last commit… top of the food chain if you will.
If it’s a local branch you can rebase:
But once the branch is being shared with anyone else it’s time to ditch the rebase and instead use merge to keep your experimental branches up to date with master.
Conclusion:
Merge works great, but creates lots of empty merge commits when you are working on a team.
Rebase keeps things tidy, but is destructive and potentially dangerous if you don’t know what you are doing.
Keep yourself safe with a simple rule:
Don’t rebase branches you have shared with another developer - they will hate you and you will hate yourself.
Do otherwise at your own peril - yes, it will hurt.
Trackbacks
Use this link to trackback from your own site.







A very concise summary of the problems with rebasing. Thank you Jarrod
Thanks I actually found this explanation to be straightforward and clear. The pictures really made the post. Good job!
I’m experienced with git, but I can imagine how this article wouldn’t be clear to newcomers. First of all, it tends to label rebase as evil (starting with the title) while other git professionals teach newcomers that rebase is better than having a messed up history of micro-merges everywhere.
So if this article still left you confused, hop over to Giant Robots blog http://robots.thoughtbot.com/post/165641717/rebase-like-a-boss
Valid criticism Mislav, but I really intended this article for people who have been using Git for awhile, but have run into some of the pitfalls that rebase can introduce to your history. That being said… thanks for putting up the link to the very well written thoughtbot article - those guys rock.
[…] #Git merge vs git rebase: avoiding rebase hell http://jarrodspillers.com/articles/git-merge-vs-git-rebase-avoiding-rebase-hell […]
[…] JarrodSpillers.com » Blog Archive » git merge vs git rebase: avoiding rebase helljarrodspillers.com […]
As a beginner user of git (only been using it for a week), I found the article to be extremely clear. I don’t expect to use rebase/merge in the near future, but the illustrations are exactly what I need to understand the difference between the two commands.
I Disagree with you Mislav, I suck at git and found this really helpful.
There is one more problem with rebase I bumped into in my practice. At my work we have one branch for each feature and when we make a release we merge all features (branches) which are released into master. But there are always features which stay unreleased for several releases. And we used to rebase them to new state of master after each release. But there are cases when we may develop one feature in two branches (subfeatures) which can be released independently (or supposed to be…)
So basically we branch from master (branch1), then create a branch branch2 from branch1. Then make some commits into branch1 and branch2 for some time. And then merge branch2 into branch1. At the same time master moves forward and we need to rebase branch1 to its new state.
And here it gets interesting. Rebase rollbacks all commits from branch1 and branch2 (since they were merged) and then recommits them chronologically(!) one by one, which means that commits from branch1 and branch2 can mix together:
10.00 branch1 commit
11.00 branch2 commit
12.00 branch1 commit
13.00 branch2 commit.
And this means that you may have had perfect merge without conflicts of branch2 into branch1 but you have conflicts between some of pairs of commits. And rebase starts to throw these conflicts into you. And once you fix one conflict and do ‘rebase –continue’ you find out that your conflict fix conflicts with next commit, you fix this just to find out next conflict… Moreover, even without conflicts you may actually end up with different code after rebase then you had before it.
Basically git-rebase can lead to conflicts which do not exist for git-merge. And this can make git-rebase very time consuming and error prone, especially if there are lots of commits over same places in file. So there is one more ‘don’t': do not rebase something that has other branches merged into it.
And, there is one ‘do’: do push (to your repository server) right after you did rebase. Otherwise, if you incidentally do pull git will bring all commits that were removed by rebase into rebased branch and create quite a mess with two threads of same commits.
All this, actually, makes git-rebase very difficult (and event dangerous) to use, not with hypothetical advantage of having more clean history in most cases.
[…] http://jarrodspillers.com/articles/git-merge-vs-git-rebase-avoiding-rebase-hell […]
[…] 23 fresh links, design, tech and seo related. okay, there’s tutorials, too :D Von zyzik Leave a Kommentar Kategorien: Uncategorized Tags: analysis, design, hacks, tech, tutorials Typographic Design Patterns and Best Practices How-To Smashing The Golden Hour Calculator Sunrise and Sunset information for pho Beautiful Single Page Website Designs: 65 New Examples Showcases 16 Cheat Sheets on 16 Essential Topics for Web Designers Pro Blog 5 Terrific Ted Talks on Future Technologies Beautiful sets of environment and nature icons Android Hacking For The Masses – Android – Gizmodo Twitter Blog: Location, Location, Location mortgages rate Unternehmen und das Netz: 10 Schritte, um im Social Web zu glnzen 100 Best Twitter Feeds for Librarians of the Future – Online Course 7 Awesome Resources to Test Cross Browser Compatibility of Your Web Listen Home A Showcase of Fantastic Footer Designs Web Design Ledger WOW: Facebook Launches a Twitter App Casas Rurales en Cantabria – Hoteles y Posadas Rurales en Santander melon agua fresca smitten kitchen Notify An awesome new Gmail Notifier for OS X Vibealicious Personas Metropath(ologies) An installation by Aaron Zinman Teach Computer Science without a computer! Computer Science Unplu Conversion PDF vers Word (DOC) 100% gratuite ! Coding Horror: The Only Truly Failed Project JarrodSpillers.com Blog Archive git merge vs git rebase: avoi […]
I learned this the hard way more than a few times. Esp when using an svn origin master, which you then modify locally, then share via github with others.
Always always keep your svn in a separate branch to only receive updates via rebase. Then pull, merge or cherry pick from your svn branch into your guthub/shared branch.
In the second diagram, blue should be above red. It’s immaterial to the merge but that’s the order in the log.
@Rick - actually i double checked it with a dummy repo and it is in fact in the correct order. If blue was put above red in diagram 2 it would mess up the upstream history and you would have to break the golden rule of rebasing from a branch that is downstream. The commit log of the authoritative branch (origin/master in this case) can never change (well it can, but bad things will happen)
Thank you for this article. Can you please tell me how does
‘git pull’ different from ‘git merge’ and ‘git rebase’ as well?
Thank you.
@n179911 - so git pull is basically exactly the same as calling “git fetch” and then calling “git merge” on the result. Passing “–rebase” to “git pull” (git pull –rebase) tells git to execute a rebase after the fetch instead of a merge.
Thanks, this made the concept abundantly clear to me, and I have read the docs before, but never really “got” it.
I will begin to use rebase more when working in master and between 2 developers.
[…] git merge vs git rebase: avoiding rebase hell — отличная статья о том, в чем разница между git merge и git rebase. Обязательно к прочтению всем, кто по долгу службы (или велению души) работает с git. […]
[…] Source:JarrodSpillers.com » Blog Archive » git merge vs git rebase: avoiding rebase hell […]
Great article, def helped me ‘get it’
> Note the green “merge commit” placed at the top of my
> hypothetical commit stack. This commit will contain the
> result of any merge conflicts that you had to resolve.
> In the case there were no merge problems it is just an
> empty commit.
No. The merge commit will reference a tree that represents the merge result, not any kind of “this conflict was resolved like that” information. Just like any other commit, it references a snapshot, so if it was an “empty” commit, that would mean that there’s nothing in the snapshot, i.e. no files.
If you have a file “foo” that contains ten lines saying “aaa” in the common ancestor, and one side of the merge adds “top” at the top, and the other side adds “bottom” at the bottom, then the merge commit will contain the file “foo” with “top” at the top, then ten lines saying “aaa” and then “bottom” at the bottom.
And if the common ancestor has a file “bar” that contains “123″, and one side changed that to “456″ and the other side changed that to “789″, then you’ll get a conflict. If you resolve that conflict so that “bar” contains “666″, then the merge commit will just contain the file “bar” with contents “666″. There’s no information that a conflict was resolved, except for the automatically generated comment in the commit message, which is for human consumption only.
You’ll only get an “empty commit” if there are actually no tracked files, which is very unlikely for a merge commit.
I’m also not sure whether the “stack” you used is a good representation, as it completely dismisses the non-linearity of the history. It makes it look like “local commit” is the only parent of “merge commit”, and like “committed by other” being the parent of “local commit”. But actually the merge commit has two parents, the “local commit” and the “committed by other” commit. And each of those has “common” as its parent.
[…] git merge vs git rebase: avoiding rebase hell (11) […]