Search code examples
gitgit-rebasegit-checkoutgit-fetch

Do these three sets of git commands do the same?


Do the following three sequences of command do the same:

Commands 1:

git fetch origin master
git rebase origin master

Commands 2:

git pull origin master --rebase

Commands 3:

git fetch origin master
git checkout FETCH_HEAD

My understanding is that all three commands do the same, which is:

  1. "Download" all the commit from the specified branch of the specified remote repository.
  2. Put the commit from the current branch "on top" of the downloaded commits.

Solution

  • In your first set of commands, the rebase probably isn't what you intended; rebase doesn't take a remote as an argument.

    (UPDATE: That said, under some circumstances git will interpret a remote name like a ref, and it's possible it will even represent what you mean. I wouldn't rely on it myself, but: If there is a symbolic ref refs/remotes/origin/HEAD - which can be interpreted as the "default branch" of origin and normally would exist if you created the local by cloning the origin at a time when it had a valid HEAD reference - then origin will expand to whatever refs/remotes/origin/HEAD points to.)

    I think you meant

    git rebase origin/master master
    

    There are shorthand ways to write that based on the upstream configuration and already having master checked out, but whatever. I'll go on assuming this is what you meant to do.

    In that case your second command is more or less a shorthand for your first set of commands.

    The third command, however, is not equivalent. Whereas rebase creates new commits and moves refs (appearing to "move" an existing set of commits), checkout does neither of those things. checkout merely moves the current HEAD.

    To illustrate, let's suppose you have

    A -- B <--(master)
                  ^HEAD
    

    and origin has

    A -- C <--(master)
    

    So if you fetch you'll get

    A -- B <--(master)
     \            ^(HEAD)
      C <--(origin/master)
    

    Now if you do a rebase as

    git rebase origin/master master
    

    (or just

    git rebase
    

    in a typical configuraiton) you'll end up with

      B
     /
    A -- C <--(origin/master)
          \
           B' <--(master)
                     ^HEAD
    

    I kept B in the diagram to illustrate why the commit at master is marked B'. The original B commit does still exist (for now), and B' is a new and separate commit created in the rebase. Because B is "dangling" it could eventually be garbage-collected.

    That's also what you could expect if, instead of fetch, you had started with

    git pull --rebase origin master
    

    On the other hand, if you don't do a rebase and instead, after a fetch, say

    git checkout FETCH_HEAD
    

    you'd get

    A -- B <--(master)
     \
      C <--(origin/master)
      ^(HEAD)
    

    No new commit, no moved ref; just HEAD changes (and you're in detached HEAD state).