Git fetch weirdness explained

2009-08-30 00:56:14 -08:00

In another tussle with Git, I performed the following sequence of commands:

  1. cd mach_star-rentzsch
  2. git fetch (from GitHub)
  3. cd ../mach_star-boredzo
  4. git fetch rentzsch (in this context, my git-remote alias for ../mach_star-rentzsch)
  5. git merge rentzsch/master

Step 5 failed with “Already up-to-date”.

What? I just fetched! I should have new commits to merge in!

Nope. For one thing, this output from step 4:

From ../mach_star-rentzsch
 * [new branch]      master     -> rentzsch/master

seems to mean “OK, here’s a new name for the master branch of that other repository, but we didn’t actually bring in any commits”.

The reason it didn’t bring in any commits is because git fetch apparently only fetches commits that are ancestors of the source repository’s current HEAD. In English: git fetch cares what you have checked out in the source repository.

It’s because I had fetched in step 2, and thereby not changed my HEAD, that step 4 did not see anything new to fetch. I don’t know why it works that way, or why they consider it useful.

Anyway, the “correct” sequence of steps is not much different: git pull, not fetch, in step 2 above. Or use Mercurial, which I’ve found makes a lot more sense in general.

It’s entirely possible that I’ve figured this out the wrong way, so take this entire explanation with a grain of salt.

6 Responses to “Git fetch weirdness explained”

  1. Kevin Ballard Says:

    I tend to use `git remote update`. That will update all branches in all remotes (or just the named remote).

  2. Mark Rowe Says:

    The reason it didn’t bring in any commits is because git fetch apparently only fetches commits that are ancestors of the source repository’s current HEAD. In English: git fetch cares what you have checked out in the source repository.

    git fetch doesn’t care what you have checked out.

    To break what happens in the two git fetchs in more specific terms:

    git fetch in your mach_star-rentzsch repository updates refs/remotes/origin/*.
    git fetch rentzsch in your mach_star-boredzo repository will update refs/remotes/rentzsch/* from refs/heads/* in your mach_star-rentzsch repository.

    Since the second fetch looks in a different location than the first fetch updated, you need to perform an extra step in between in order for the second fetch to see any new changes.

    Anyway, the “correct” sequence of steps is not much different: git pull, not fetch, in step 2 above. Or use Mercurial, which I’ve found makes a lot more sense in general.

    It’s entirely possible that I’ve figured this out the wrong way, so take this entire explanation with a grain of salt.

    It feels to me like you’re trying to use git in the manner that you’d use Mercurial, rather than using git in the way that it is most naturally used. There’s no need to use an extra local repository to track a remote repository, and doing so only makes the process more cumbersome. Is there some reason you’re doing this rather than adding rentzsch’s GitHub repository as a remote directly in your mach_star-boredzo repository? This would simplify the process down to:
    git pull rentzsch master

  3. Peter Hosey Says:

    To break what happens in the two git fetchs in more specific terms:

    git fetch in your mach_star-rentzsch repository updates refs/remotes/origin/*.
    git fetch rentzsch in your mach_star-boredzo repository will update refs/remotes/rentzsch/* from refs/heads/* in your mach_star-rentzsch repository.

    Since the second fetch looks in a different location than the first fetch updated, you need to perform an extra step in between in order for the second fetch to see any new changes.

    And the implicit git merge of git pull does that extra step of updating refs/heads/* from refs/remotes/origin/*?

    It feels to me like you’re trying to use git in the manner that you’d use Mercurial, rather than using git in the way that it is most naturally used.

    Guilty. I have not yet seen a document that clearly and concisely explains the concepts of Git and the definitions of its commands (the manpages do not qualify), and Git is certainly not obvious.

    Is there some reason you’re doing this rather than adding rentzsch’s GitHub repository as a remote directly in your mach_star-boredzo repository?

    I like having a separate repository that is purely a mirror of rentzsch’s repository.

    This would simplify the process down to:

    git pull rentzsch master

    I’ve generally adhered to keeping fetch (pull in hg) and merge separate, because it’s what I’m used to. In retrospect, you’re right that I should make myself more comfortable with fetching and merging in one command.

    Also, the ability to select the source repository, source branch, and destination branch in one command is frustratingly well-concealed. The manpage does say “refspec” at the top, but doesn’t define this for another two and a half pages (and that’s on my long terminal).

    Since I’m working on a different branch in my repo, I should say master:leopard-64bit for the last argument, correct? Or should I just not worry about it, and then drag the leopard-64bit tag up in GitX to match?

  4. Mark Rowe Says:

    And the implicit git merge of git pull does that extra step of updating refs/heads/* from refs/remotes/origin/*?

    git pull will only update the current branch.

    I like having a separate repository that is purely a mirror of rentzsch’s repository.

    It’ll make your life easier if you don’t try and merge from this separate repository, but instead work with the remote in your repository as I mentioned. You can continue to keep a separate repository around to make it easier to see the state of rentzsch’s repository.

    I’ve generally adhered to keeping fetch (pull in hg) and merge separate, because it’s what I’m used to. In retrospect, you’re right that I should make myself more comfortable with fetching and merging in one command.

    If you added rentzsch’s GitHub repository as a remote directly in your mach_star-boredzo repository then the explicit fetch and merge would still be very simple:

    git fetch rentzsch
    [ … look over rentzsch/master to verify that it is as we expect … ]
    git merge rentzsch/master

    Since I’m working on a different branch in my repo, I should say master:leopard-64bit for the last argument, correct? Or should I just not worry about it, and then drag the leopard-64bit tag up in GitX to match?

    git pull rentzsch master will merge the remote master branch in to your current local branch. You don’t need to explicitly mention the local branch name to do this as git pull always merges in to the current branch.

    The right side of the refspec used by pull is the destination of the fetch, not the resulting merge. This means that you almost certainly don’t want to do git pull rentzsch master:leopard-64bit as that will result in the remote master branch being fetched in to leopard-64bit and then merged in to your current branch.

    And yes, the Git documentation is very dense. The Git tutorial touches briefly on fetch, pull, and remotes briefly which may be useful to look over if you’ve not seen it already.

  5. Peter Hosey Says:

    The right side of the refspec used by pull is the destination of the fetch, not the resulting merge. This means that you almost certainly don’t want to do git pull rentzsch master:leopard-64bit as that will result in the remote master branch being fetched in to leopard-64bit and then merged in to your current branch.

    My current branch is leopard-64bit.

  6. Mark Rowe Says:

    Sure, and that’s where you want the merge to take place. You don’t want the state of the leopard-64-bit branch to be blown away by the fetch portion of the pull command otherwise there’ll be nothing left on the branch to merge.

Leave a Reply

Do not delete the second sentence.