9

I have a local repository that pulls from a remote one. Running git pull as well as git fetch; git merge FETCH_HEAD used to perform exactly the same action, as is expected from the description of git pull:

DESCRIPTION

Incorporates changes from a remote repository into the current branch. In its default mode, git pull is shorthand for git fetch followed by git merge FETCH_HEAD.

Presently, and unexpectedly, running git fetch stopped updating the FETCH_HEAD reference correctly. FETCH_HEAD is now stuck to an old commit. Running git fetch downloads all changes to remote tracked branches, but FETCH_HEAD remains unchanged regardless of the branch in which it is run.

# currently in branchone
> git fetch

# branchone is up to date since...
> git rev-parse branchone
593539e8a98ba5980d4b645db3b0f506bb9b6a2c

# ...its in the same commit as the remote branch
> git rev-parse origin/branchone
593539e8a98ba5980d4b645db3b0f506bb9b6a2c

# however FETCH_HEAD shows something different
> git rev-parse FETCH_HEAD
37301df96597ac037f8e7e846fea6fc7df77bea5

git pull still performs the correct task. However running git fetch; git merge FETCH_HEAD will do something different since FETCH_HEAD points to an incorrect commit.

Is there any setting or issue that could be messing with git fetch behavior?

Maic López Sáenz
  • 8,876
  • 4
  • 40
  • 55

3 Answers3

12

Running git fetch without any options will fetch all references in the remotes and write them to the .git/FETCH_HEAD file. The contents of the file usualy looks something like this:

37301df96597ac037f8e7e846fea6fc7df77bea5 branch 'master' of github.com:user/repo
593539e8a98ba5980d4b645db3b0f506bb9b6a2c not-for-merge branch 'branchOne' of github.com:user/repo

When you have a file like this under the .git directory, you can use it as a reference as long as the first thing in that file is either a 40 character hex number, or a shorter hex number that actualy matches to an existing commit.

# This file can be used as a reference
> cat .git/MAGIC_HEAD
deadbeefdeadbeefdeadbeefdeadbeefdeadbeef lorem ipsum
the rest does not really matter
refrigerator

# And thus it will be interpreted by many git commands like this
> git rev-parse MAGIC_HEAD
deadbeefdeadbeefdeadbeefdeadbeefdeadbeef

Knowing this we can see that after running git fetch the reference FETCH_HEAD will resolve to is whatever happens to be in that first line

# Assuming the already mentioned contents of .git/FETCH_HEAD
> git rev-parse FETCH_HEAD
37301df96597ac037f8e7e846fea6fc7df77bea5

Seems like the order of the contents of .git/FETCH_HEAD is not guaranteed to contain first the reference for the current branch.

By trying it in different repositories it seems that in some the first line is always the current branch, and thus git fetch; git merge FETCH_HEAD works as expected. On other repositories however the contents of .git/FETCH_HEAD will be ordered differently and often the first line will be a reference to the remote commit of a different branch, thus making the FETCH_HEAD reference incorrect.

Why it behaves differently is a mystery to me.

As a solution, if git fetch remote_name branch_name is used only this specific branch is fetched and only that single line will appear in the contents of .git/FETCH_HEAD, making the FETCH_HEAD reference always correct.

# Will only fetch branchone
> git fetch origin branchone

# FETCH_HEAD will contain only a single line
> cat .git/FETCH_HEAD
593539e8a98ba5980d4b645db3b0f506bb9b6a2c branch 'branchOne' of github.com:user/repo
Maic López Sáenz
  • 8,876
  • 4
  • 40
  • 55
0

Just try forcing your head to point to the latest commit / push which has been done.

Use this on your GIT Repository:

git reset --hard HEAD@{1}

Hoping this can solve your problem, taking this to a point where it used to work perfectly as before.

aliasgar
  • 359
  • 4
  • 20
  • Sadly no. Even reseting the repository to very old revisions changes nothing in the behavior of `git fetch` and `FETCH_HEAD`. – Maic López Sáenz Jul 16 '12 at 19:24
  • Another thing you can try is, delete the entire local repository, and clone it again. If not, i'll help you further. I am trying to gauge what is wrong with you local repository.. – aliasgar Jul 17 '12 at 06:34
  • On a new repository the behavior is the same. The commit that `FETCH_HEAD` points to is the first that appears in the `.git/FETCH_HEAD` file. Reading around it seems like this is the intended behavior, but I am still left with the doubt of why previously doing `git fetch; git merge FETCH_HEAD` worked perfectly on any branch. – Maic López Sáenz Jul 24 '12 at 02:02
  • pull = fetch + merge, so it will always work. fetch only brings the latest code to ur local repo. so if there is a commit from some other place, ur fetch_head will point to the latest, and hence pointing to different places. it should work if there is no commit, after you have fetched, and then git merge FETCH_HEAD would work. I assumed ur the only one committing to ur github account. Ideally u should not be using them separately, use git pull which is fetch + merge – aliasgar Jul 25 '12 at 07:13
0

After you run git fetch (without arguments), FETCH_HEAD will only include a reference valid for merging (i.e. not marked as not-for-merge) if the current local branch (i.e. HEAD) is a tracking branch.

The solution is to either make the current branch a tracking branch (see How do you make an existing Git branch track a remote branch?) or specify a remote and a branch to fetch (i.e. git fetch origin branch

Community
  • 1
  • 1
fons
  • 4,057
  • 2
  • 25
  • 46
  • The branches I was working on where all branches already tracking a remote branch. After you brought to my attention this question again I redid my answer to explain better why the `FETCH_HEAD` reference fails to update correctly. Maybe it does have some relevance what you mention, and the first line in `.git/FETCH_HEAD` is only guaranteed under some other circumstance that I am not aware of. – Maic López Sáenz Apr 11 '14 at 04:10