This is mostly of the nature of a curiosity as I'm trying to get familiar with Git. I have looked at the documentation for 'git fetch' but I don't see an obvious explanation for the below. Thanks in advance, and apologies if this is howlingly obvious.
1) From a central repository, say GitHub, I clone a repository named website
on each of two machines, HostA
and HostB
.
2) on HostA
, I make a change to a file, say README.txt
, and commit it.
At this point on HostA
, the commits for branches master
and origin/master
are, as expected different since I haven't pushed yet
git show master git show origin/master
report different hashes (since master
has the change and origin/master
does not)
3) Once I push, they are after that the same.
4) Now, over on HostB
, if I do the following:
git fetch git merge FETCH_HEAD
afterwards, on HostB master
and origin/master
report the same hash when queried with git show
BUT
if instead I had done, on HostB
:
git fetch origin master git merge FETCH_HEAD
at that point the hashes still differ.
git show origin git show origin/master
report different hashes
The tracking branch origin/master
isn't updated until I do a plain git fetch
Why is this?
git pull is a Git command used to update the local version of a repository from a remote. It is one of the four commands that prompts network interaction by Git. By default, git pull does two things. Updates the remote tracking branches for all other branches.
The git fetch command downloads commits, files, and refs from a remote repository into your local repo. Fetching is what you do when you want to see what everybody else has been working on.
git fetch is the command that tells your local git to retrieve the latest meta-data info from the original (yet doesn't do any file transferring. It's more like just checking to see if there are any changes available). git pull on the other hand does that AND brings (copy) those changes from the remote repository.
`git pull origin master` fetches commits from the master branch of the origin remote (into the local origin/master branch), and then it merges origin/master into the branch you currently have checked out. `git pull` only works if the branch you have checked out is tracking an upstream branch.
If your branch has an associated remote tracking branch that means its configuration is like:
git config branch.[branch-name].remote [remote-name] git config branch.[branch-name].merge [remote-master]
The key part of git fetch
which explain the difference between the two commands is:
<refspec>
The format of a
<refspec>
parameter is an optional plus+
, followed by the source ref<src>
, followed by a colon:
, followed by the destination ref<dst>
.
The remote ref that matches<src>
is fetched, and if<dst>
is not empty string, the local ref that matches it is fast-forwarded using<src>
.
Let me repeat it:
if <dst>
is not empty string, the local ref that matches it is fast-forwarded using <src>
.
Knowing that:
git fetch
is equivalent to git fetch origin master:master
(from the default value of your branch config), so it will update the remote tracking branch: the destination of the refspec is specified for you.
git fetch origin master
is equivalent to "git fetch origin master:
", not to "git fetch origin master:master
"; it stores fetched value of 'master
' branch (of remote 'origin
') in FETCH_HEAD
, and not in 'master
' branch or remote-tracking 'remotes/origin/master
' branch (from Jakub Narębski's answer)
In other words, you didn't specify the destination of your refspec
The answer lies in the messages you get back from git fetch
. In the first case, when you fetch without providing a refspec, you'll see that the remote tracking branches are updated:
remote: Counting objects: 5, done. remote: Total 3 (delta 0), reused 0 (delta 0) Unpacking objects: 100% (3/3), done. From /depot c67d1c8..1941673 master -> origin/master
Note how the message says that origin/master is updated with the master from the origin.
Now in the second case, where you specify the refspec, you get something altogether different:
remote: Counting objects: 5, done. remote: Total 3 (delta 0), reused 0 (delta 0) Unpacking objects: 100% (3/3), done. From /depot * branch master -> FETCH_HEAD
So when you specify the refspec, the remote tracking branch (origin/master) is NOT updated, only FETCH_HEAD.
The end result is that you'll appear to be ahead of origin/master when you're not really. I can't imagine why this behavior would be desirable, but it's definitely an interesting little quirk of the fetch command.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With