What's the difference between 'git merge' and 'git rebase'?

216,870

Solution 1

Suppose originally there were 3 commits, A,B,C:

A-B-C

Then developer Dan created commit D, and developer Ed created commit E:

A-B-C-D-E

Obviously, this conflict should be resolved somehow. For this, there are 2 ways:

MERGE:

A-B-C-D-E-M

Both commits D and E are still here, but we create merge commit M that inherits changes from both D and E. However, this creates diamond shape, which many people find very confusing.

REBASE:

A-B-C-D-E-R

We create commit R, which actual file content is identical to that of merge commit M above. But, we get rid of commit E, like it never existed (denoted by dots - vanishing line). Because of this obliteration, E should be local to developer Ed and should have never been pushed to any other repository. Advantage of rebase is that diamond shape is avoided, and history stays nice straight line - most developers love that!

Solution 2

Personally I don't find the standard diagramming technique very helpful - the arrows always seem to point the wrong way for me. (They generally point towards the "parent" of each commit, which ends up being backwards in time, which is weird).

To explain it in words:

  • When you rebase your branch onto their branch, you tell Git to make it look as though you checked out their branch cleanly, then did all your work starting from there. That makes a clean, conceptually simple package of changes that someone can review. You can repeat this process again when there are new changes on their branch, and you will always end up with a clean set of changes "on the tip" of their branch.
  • When you merge their branch into your branch, you tie the two branch histories together at this point. If you do this again later with more changes, you begin to create an interleaved thread of histories: some of their changes, some of my changes, some of their changes. Some people find this messy or undesirable.

For reasons I don't understand, GUI tools for Git have never made much of an effort to present merge histories more cleanly, abstracting out the individual merges. So if you want a "clean history", you need to use rebase.

I seem to recall having read blog posts from programmers who only use rebase and others that never use rebase.

Example

I'll try explaining this with a just-words example. Let's say other people on your project are working on the user interface, and you're writing documentation. Without rebase, your history might look something like:

Write tutorial
Merge remote-tracking branch 'origin/master' into fixdocs
Bigger buttons
Drop down list
Extend README
Merge remote-tracking branch 'origin/master' into fixdocs
Make window larger
Fix a mistake in howto.md

That is, merges and UI commits in the middle of your documentation commits.

If you rebased your code onto master instead of merging it, it would look like this:

Write tutorial
Extend README
Fix a mistake in howto.md
Bigger buttons
Drop down list
Make window larger

All of your commits are at the top (newest), followed by the rest of the master branch.

(Disclaimer: I'm the author of the "10 things I hate about Git" post referred to in another answer)

Solution 3

While the accepted and most upvoted answer is great, I additionally find it useful trying to explain the difference only by words:

merge

  • “okay, we got two differently developed states of our repository. Let's merge them together. Two parents, one resulting child.”

rebase

  • “Give the changes of the main branch (whatever its name) to my feature branch. Do so by pretending my feature work started later, in fact on the current state of the main branch.”
  • “Rewrite the history of my changes to reflect that.” (need to force-push them, because normally versioning is all about not tampering with given history)
  • “Likely —if the changes I raked in have little to do with my work— history actually won't change much, if I look at my commits diff by diff (you may also think of ‘patches’).“

summary: When possible, rebase is almost always better. Making re-integration into the main branch easier.

Because? ➝ your feature work can be presented as one big ‘patch file’ (aka diff) in respect to the main branch, not having to ‘explain’ multiple parents: At least two, coming from one merge, but likely many more, if there were several merges. Unlike merges, multiple rebases do not add up. (another big plus)

Solution 4

Git rebase is closer to a merge. The difference in rebase is:

  • the local commits are removed temporally from the branch.
  • run the git pull
  • insert again all your local commits.

So that means that all your local commits are moved to the end, after all the remote commits. If you have a merge conflict, you have to solve it too.

Solution 5

For easy understand can see my figure.

Rebase will change commit hash, so that if you want to avoid much of conflict, just use rebase when that branch is done/complete as stable.

enter image description here

Share:
216,870
Daniel Peñalba
Author by

Daniel Peñalba

Software Engineer at Unity Technologies, Valladolid, Spain. Currently developing gmaster, Plastic SCM and SemanticMerge. Areas: C# GUI development Winforms and WPF expert ASP .NET Core Multiplatform UI development with Mono (Linux and OSX, GTK# and MonoMac) Eclipse plugin, Java Automated testing, NUnit, Moq, PNUnit and TestComplete Email: dpenalba[AT]codicesoftware[DOT]com I play the guitar at Sharon Bates, the greatest Spanish rock band.

Updated on September 28, 2021

Comments

  • Daniel Peñalba
    Daniel Peñalba over 2 years

    What's the difference between git merge and git rebase?

  • Uwe Geuder
    Uwe Geuder over 10 years
    Nice illustrations. However, I do not fully agree with the positive tone that rebase is handled. In both merge and rebase conflicts can occur that need manual resolution. And as always when programmers are involved there is a non-neglectable chance of errors aka bugs. If a merge error happens the whole team or community can see the merge and verify whether a bug was introduced there. The history of the rebase stays in 1 developer's repo and even there it has only limited lifetime in the reflog. It might look nicer, but nobody else can see as easily what went wrong.
  • Greg Maletic
    Greg Maletic almost 10 years
    > "However, this creates diamond shape, which many people find very confusing." Um...can you elaborate?
  • mvp
    mvp almost 10 years
    @GregMaletic: Diamond shape is non-linear history. I don't know about you, but I do not like non-linear things in general. That said, you are welcome to use merge with diamonds if you really prefer it - nobody is forcing you.
  • Vortex
    Vortex about 7 years
    While this answer is extremely helpful, it would be better if you added actual git commands with simple foo.txt files to reproduce it locally. Like last user said, it's not obvious who's doing rebase.
  • mvp
    mvp about 7 years
    @Vortex: the whole idea is to give you explanation of the concept. Adding any git commands would just obscure what's really happening.
  • pferrel
    pferrel over 5 years
    Perhaps a better comparison is git merge --squash and git rebase, which both try to create a history that shows code that actually existed in a single place together. Git merge (or pull) will interleave commits by date, looking like certain commits existed together (when there is no way to tell if they ever did) and implying the code at any date actually works together (there is no such implication nor is it often true). IMO git merge no --squash should never be used, it comes just short of creating lies that only the most circumspect engineer can see through.
  • mvp
    mvp over 5 years
    @pferrel: l don't think you got it correctly. git merge does not interleave commits (but it might appear so by looking at git log). Instead, git merge keeps both development histories by Dan and Ed preserved intact, as it was seen from each one point of view at a time. git rebase makes it look like that Dan worked on it first, and Ed followed him. In both cases (merge and rebase), actual resultant file tree is absolutely identical.
  • ATL_DEV
    ATL_DEV over 4 years
    @mvp, You explanation doesn't explain why Git keep Dan's commit and not Ed's.
  • mvp
    mvp over 4 years
    @ATL_DEV: both commits are kept in history. If rebase was done by Ed, then history would appear D first, E second. If Dan was doing rebase, it would be E then D. But, regardless of order, final state is exactly the same, and is equivalent to merge. For merge, both points of view are kept intact, and it is impossible to say which one was done first.
  • ATL_DEV
    ATL_DEV over 4 years
    @mvp. Git rebase operation appears to be superfluous. If the only real difference is cosmetic, then whether it's a straight or not should really be determined in how it is visually rendered on the screen. In a sense, it's like directories in a file system which are only useful for human organization, but serves no purpose in the actual file structure implementation.
  • mvp
    mvp over 4 years
    @ATL_DEV: it's not just cosmetic. Because merge keeps both development histories intact, it makes harder for people to understand what's going on. Rebase linearizes this natural conflict on who was there first.
  • ATL_DEV
    ATL_DEV over 4 years
    @mvp, just to be clear, I don't see why it should linearize the conflicts at the repo level when it can keep always keep the histories intact but linearize the tree only when it presents the tree visually.
  • gangmax
    gangmax over 3 years
    @mvp, in your answer you said "for git rebase, ...we get rid of commit E, like it never existed (denoted by dots - vanishing line)", but in your reply to "ATL_DEV", you said "for git rebase, ...both commits are kept in history...". I'm confused here: for git rebase, if "both commits are kept in history", did you really "get rid of commit E, like it never existed"? where can you see the history of commit E?
  • mvp
    mvp over 3 years
    @gangmax, commit E is removed by rebase, but changes intended by that comment live on in R. Difference between R and D is the same as difference between E and C. For merge, commit E is fully preserved in history, but that history becomes forked, which could make it more difficult to understand what's really going on.
  • gangmax
    gangmax about 3 years
    @mvp, thank you for the detailed explanation.
  • IMSoP
    IMSoP almost 3 years
    Commit digrams have pointers pointing to parents not children, because that's what git stores. Understanding that - and the fact that a branch is a pointer to a single commit at its "tip" - is incredibly useful for understanding many things that git does. I've also no idea what you mean by "GUI tools for Git have never made much of an effort to present merge histories more cleanly, abstracting out the individual merges". Your actual descriptions of rebase and merge are great though.
  • IMSoP
    IMSoP almost 3 years
    "closer to a merge" than what?