What's the difference between `git fetch` then `git rebase`, and `git pull --rebase`?

20,274

Solution 1

The rule with Git is that you should never attempt to change history after it has been shared, published, or pushed. You can do so, of course, if you really want to and have sufficient permissions, but it should be done with great care since it can mess other people up.

Now fortunately when you have a typical Git deployment with a single upstream repository (origin) which is the source of all that is good and true in the universe, you can use git pull --rebase to your heart's content and it will be perfectly safe and in my opinion give you a much more sane (meaning linear) history. I and my team use it continuously.

However, if you start having multiple remotes and start doing git pull --rebase <arguments> so that you are no longer rebasing against the same target every time, or start pushing your branch to alternate repositories before running git pull --rebase with your primary upstream—then you can start running into troubles.

Any time where you share your changes with another remote/repository and then change those changes (for values of changing equal to changing the SHA, parent, etc. even if the commit message/content did not change), you can mess up the person who had the old changes.

As long as you don't fall outside the envelope of rebase sanity, git pull --rebase will be very good for you.

That, err, doesn't answer the question about the difference between git pull --rebase and git fetch && git rebase @{u}. I'll just go ahead and say that I am unaware of any difference and if there is one, it is subtle enough that I have not noticed it in the years I have used Git. Possibly in that the system figures out the correct repository your branch should fetch if you have multiple repositories and "origin" isn't the upstream of this branch?

And even if you do go very awry with git-rebase, you can of course recover yourself back to your original pre-rebase environment easily with git log -g and/or git reset --hard ORIG_HEAD. Just don't do force pushes (disallowed by default in almost all Git servers), and you will be happy happy.

EDITED

With time my understanding has expanded. git pull --rebase calls git rebase to do the rebase work, so in that sense there is no difference between them. However, git-pull actually calls git rebase --onto @{u} $(git merge-base HEAD @{u}@{1})

OK, that syntax ("@{u}@{1}") is perhaps a little opaque and is a simplification to boot, but the point is that it finds out what the merge base was to upstream BEFORE it ran the fetch command. What difference does this make, you ask?

Well, in the normal case none. However, if you are changing where upstream is pointing to or if upstream itself was rebased, quite a lot. If upstream was rewritten and then you did a git rebase @{u} you could be very unhappy and could get double-commits or conflicts depending on how much the older commits were rewritten.

However, with the magic behind git pull --rebase only the commits which are yours and yours alone will be applied on top of @{u}.

OK, this too is a simplification. If upstream did a rebase starting with the 100 commits ago (but there are actually 101+ commits in history) and you did a git fetch before doing a git pull --rebase then Git will not be able to accurately determine what the proper historical merge-base was to figure out what your local commits are.

The upshot of which is, git fetch is considered harmful (when you have local commits and upstream is rewritten). However, the real rule-of-thumb is "never attempt to change history after it has been shared, published, or pushed" which is where I started.

TL;DR:

git fetch is considered harmful (so use git pull --rebase); and never attempt to change history after it has been shared, published, or pushed (because, among other things, it will cause git fetch to be harmful).

Solution 2

The truth is that they ARE different. Here's a really helpful web page which explains it beautifully:

http://gitolite.com/git-pull--rebase.html

So git pull --rebase has some significant magic over git fetch; git rebase which most of the time you won't notice, but in cases where the upstream maintainer has naughtily ignored all those stern warnings and decided to rewrite the history of a public branch, it can really help out by consulting your local reflog and doing the local rebase in a more intelligent way.

That said, this is still a rebase, so you are still rewriting history! Therefore all the standard stern warnings still apply. But if you're working on a private (i.e. unpublished) branch, then that's OK.

I'll say a bit more regarding the stern warnings. They are valid, but personally I find most people just a bit too paranoid about rebase, like a git rebase crept into their bedroom in the middle of the night when they were young and ate their sister or something. It really shouldn't be that scary:

  • if it's a private branch, rebase to your heart's content
  • if it's a public branch don't rebase unless you really have to, and if you do, make sure you understand the impact, and make sure anyone who might be impacted is properly informed about what you've done, so they don't get a nasty surprise and waste a load of time figuring out what happened.

It's that simple. And yes, I would go as far as actively encouraging people to regularly git rebase -i on their private branches. Polishing history before pushing to somewhere public/upstream is a good thing, because no one wants to wade through a project's history which is full of commits like 'oops, fixing a mistake I made 3 commits ago'. (OTOH, don't get totally obsessed with rebasing in quest of a flawless history. We're human. We make mistakes. Deal with it.)

One last observation regarding the git pull --rebase magic. If the upstream public branch has been rebased in a sensible way (e.g. squashing / fixing up commits, or dropping commits which shouldn't have been put there) then the magic works in your favour. However if the upstream rebase accidentally dropped commits, then the magic will silently prevent you from putting them back. In this case if you want to put back those dropped commits, you should instead use git fetch; git rebase.

Share:
20,274

Related videos on Youtube

andriy
Author by

andriy

A .NET developer in the Germany/France/Switzerland border area. I do web development, desktop development, Selenium automation, and lots more. Currently employed at Dreamlines GmbH. I can help you transition your team to Git automate your regression tests release your next great ASP.NET MVC, WPF, or even WinForms app chase down those hard-to-catch bugs write tests for things that are hard to test I speak fluent English, excellent Romanian, and pretty good German. And a smattering of French, at least enough to tell the airport taxi drivers how to find my house.

Updated on January 28, 2020

Comments

  • andriy
    andriy over 4 years

    In reading the git pull page, it gives this stern warning about git pull --rebase:

    This is a potentially dangerous mode of operation. It rewrites history, which does not bode well when you published that history already. Do not use this option unless you have read git-rebase(1) carefully.

    In the git rebase page, it gives a lot of description but no warning of this sort.

    In addition, I've seen some people say that

    git fetch
    git rebase
    

    is the same as

    git pull --rebase
    

    while others say they're slightly different.

    What's the truth?

    • Fredrik Pihl
      Fredrik Pihl about 13 years
      Really good book about git that you should keep under your pillow progit.org
    • andriy
      andriy about 13 years
      The warning on the git pull page makes it sound like things can go awry quite easily. But the description in the book makes it sound like you have to make an effort to screw up rebasing.
    • andriy
      andriy over 11 years
      What I've ended up doing regularly is using git fetch to update my view of the remote repo, and then looking at the pulled-down history and deciding what I want to do. (Usually I follow it up with a git rebase.)
    • Michael Freidgeim
      Michael Freidgeim over 8 years
  • Rick O'Shea
    Rick O'Shea almost 8 years
    TL;DR (perhaps the answer to the questions are buried in there but I'll never know:)
  • Adam Spiers
    Adam Spiers almost 8 years
    This doesn't answer the question at all. Additionally it appears that you downvoted my correct answer despite not having the patience to read it.
  • Seth Robertson
    Seth Robertson almost 8 years
    @RickO'Shea: Added a TL;DR just for you.
  • mvp
    mvp almost 8 years
    TLDR is wrong: git fetch cannot be possibly harmful! It does not do anything but to get new objects from remote. It does not change state of local checkout or local branches in any way.
  • Seth Robertson
    Seth Robertson almost 8 years
    @mvp: You are correct that fetch does not change the local branches, but that doesn't stop it from being harmful (under the specific circumstances I outlined in the paragraph above the TL;DR). If you think I am ''now'' wrong about those circumstances (I have not checked this part of git recently) please dispute the technical details, not the broad summary.
  • Alaa Ali
    Alaa Ali over 7 years
    @SethRobertson perhaps what @mvp meant is that your TL;DR should be a summary of your answer to the full question "What's the difference between git fetch then git rebase, and git pull --rebase?" and not only a summary of your last paragraph, because as it stands right now, anyone glancing over your answer looking for a TL;DR will read "git fetch is considered harmful? I thought git fetch just fetches and doesn't change anything? And how does that answer the question of git pull --rebase / git rebase?"
  • mvp
    mvp over 7 years
    @AlaaAli: yes, that's exactly what I meant: git fetch cannot possibly be harmful by itself. As it stands, this answer does not make any sense, at least to me.
  • Seth Robertson
    Seth Robertson over 7 years
    @mvp: You are right, git fetch is not harmful by itself. It is harmful because it causes git to lose information that allows subsequent commands like git pull --rebase or git rebase to work in the best possible way if someone else rewrote history. You can normally get away with it because all three circumstances rarely happen at the same time.
  • Seth Robertson
    Seth Robertson over 7 years
    @AlaaAli: Expanded the TL;DR for you to include a hint as to why the git fetch is harmful as explained in the full answer.
  • mvp
    mvp over 7 years
    @SethRobertson: unfortunately, I still don't buy it.