Having a hard time understanding git-fetch
Asked Answered
O

4

60

I am having a hard time understanding the nuances of git-fetch. I understand that doing a fetch, fetches the remote refs into a local tracking branch.

I have a few questions though:

  1. Can it be possible that a local tracking branch does not exist? If so, will it then be created automatically?

  2. What will happen if I do a fetch and specify a non tracking branch as the destination?

  3. The man page for git-fetch specifies:

    git-fetch <options> <repository> <refspec>
    

How would I use the refspec to fetch contents from my remote master into its remote tracking branch? I believe this may be possible if my current HEAD is on master and I run

git fetch origin master

However, can I use the <+?src:dest> refspec to achieve the same thing? I think this will help me understand the concepts better.

And one more question:

My .git/config file has the following line for fetching (showing only relevant lines):

fetch = +refs/heads/*:refs/remotes/origin/*

Can someone please explain what this line exactly means?

Oracle answered 1/7, 2009 at 17:54 Comment(1)
Since Git 2.1 (August 2014), there are additional information on git fetch. See my answer belowPennington
D
67

First, there's no such concept of "local tracking" branches, only "remote tracking" branches. So origin/master is a remote tracking branch for master in the origin repo.

Typically you do git fetch $remote which updates all your remote tracking branches, and creates new ones if needed.

However, you can also specify a refspec, but that will not touch your remote tracking branches, instead, it will fetch the branch you specified and save it on FETCH_HEAD, unless you specify a destination. In general you don't want to mess with this.

Finally:

fetch = +refs/heads/*:refs/remotes/origin/*

That means if you do

git fetch origin

It will actually do:

git fetch origin +refs/heads/*:refs/remotes/origin/*

Which means a remote heads/foobar will be the local remotes/origin/foobar, and the plus sign means they'll be updated even if it's non-fast-forward.

So your local origin/foobar will always be the same as foobar on the origin remote (after running git fetch). And origin/master will obviously be the same as master on the origin remote. So will all the other remote tracking branches.

Dolhenty answered 1/7, 2009 at 19:18 Comment(9)
I was about to start answering this question when you added this response. I couldn't have said it better. +1Tent
Hi felipic, Thanks for your answer. It helped clear many of my doubts. One question about the remote tracking branch. If I understand correctly, the remote tracking branch exists in my current repository and is meant to track the code which exists in some other (the remote) repository. If this is true, then would it be proper to say that the remote tracking branch is like a proxy of the actual branch which exists in the remote repository?Oracle
Yes, exactly, it's meant to be like a cached view of the remote repo that you can update at will.Dolhenty
Thanks Felipic, Thanks. One more question. If I want to refer to the actual remote repo (for the purpose of diffing my remote tracking branch with the actual remote branch), then can I do it with origin/master, or should I use the URL of the remote repo to refer to it? git diff origin/master refs/remotes/origin/master -- Thanks ParagOracle
origin/master is a shorthand of refs/remotes/origin/master.Dolhenty
FelipeC - Your comment "There's no such concept of local tracking branches, only remote tracking branches." is interesting b/c there's such conflicting terminology around branches. This often referenced link web.archive.org/web/20130419172453/http://www.gitguys.com/… distinguishes between "tracking branches" & "remote tracking branches". They call origin/master a "remote tracking branch" - I agree - but then they call "master" a "tracking branch" too. What is master tracking? Are they wrong or is it a terminology problem?Vivienne
@Vivienne Their terminology is confusing. The local branch "master" is just that: a local branch. They got the remote tracking branch right: "origin/master". What the article is talking about is the upstream tracking branch, but the right way to think about it is the other way around: "origin/master" is the upstream of "master": it is a property of the "master" branch. It's not the other way: "master" is not the tracking branch of "origin/master". felipec.wordpress.com/2013/09/01/…Dolhenty
@Dolhenty I feel better now - they were flat wrong in their terminology. I've heard people say "local branch master tracks the remote tracking branch origin/master which tracks the remote branch master on origin." Ugh, master does not "track" origin/master and master is not a tracking branch.Vivienne
@Vivienne I wouldn't say they are wrong, it's a matter of perspective. git status can say your 'master' branch is behind 'origin/master' because 'origin/master' is the upstream tracking branch of 'master', so you can say 'master' does track 'origin/master', except it's just certain commands that check the upstream tracking branch, so it can be said that it is a "tracking branch". To me it's much more useful to think that it has a tracking branch, rather than it is one, because you can just ignore the upstream tracking branch, or even remove it, 'master' will keep working just the same.Dolhenty
P
25

felipec have answered most of issues in question in his answer.

A few remaining (most taken from git fetch manpage; which is a bit dated in some places, unfortunately):

  • If remote-tracking branch (branch which tracks some branch in some remote repository) does not exists, it would be created.

  • The branch you fetch into (the <dst> in [+]<src>:<dst>) doesn't need to reside in remotes/<remote>/ namespace. For example for mirroring repositories (git clone --mirror) refspec is 1 to 1. In old days before separate remotes layout (before remotes/<remote>/ namespace for remote-tracking refs) master branch was fetched into branch called origin. Even currently tags are fetched directly into tags/ namespace in mirroring fashion.

  • If branch you are fetching into (the right hand side of refspec <src>:<dst> does exist, Git would check if download would result in fast-forward, i.e. if current state in <dst> is ancestor of state in <src> in given remote repository. If it isn't, and you don't use -f/--force option to git-fetch, or prefix refspec with '+' (use +<src>:<dst> refspec) fetch would refuse to update that branch.

  • 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. It can be followed by git merge FETCH_HEAD. Usually not used directly, but as part of one-time pull without setting remote-tracking branch: git pull <URL> <branch>.

  • +refs/heads/*:refs/remotes/origin/* as value for remote.origin.fetch configuration variable means that each branch (ref in refs/heads/ namespace) in remote origin is fetched into respectively named remote-tracking branch in refs/remotes/origin/ namespace, e.g. master branch in origin (i.e. refs/heads/master ref) would be fetched into origin/master remote-tracking branch (i.e. refs/remotes/origin/master ref). The '+' prefix means that fetch would succeed even in non fast-forward case, which means when branch on remote is rebased, or rewound (reset to some state in past) or otherwise amended.

Sidenote: You would probably want to use higher level git remote command to manage remote repositories and get updates.

Peashooter answered 1/7, 2009 at 20:44 Comment(5)
Yes, "git remote update" is really handy, especially when you have multiple remotes.Gamecock
Hi Jakub, Thanks for the answer. In point #4 (para starting with "git fetch origin master"), you say that the remote repository will be stored in origin and not in remotes/origin/master. Is this because the command translates into "git fetch origin master:" which does not have a destination? Now if I want to merge the contents, then should I merge with FETCH_HEADS? Is this the correct way to fetch or should we ensure that the remote contents are fetched into the refs/remotes/origin/master?Oracle
@blog.adaptivesoftware.biz: I edited the answer to try to explain it better. Usually it is better workflow to set remote tracking branches (using "git remote add") when you plan fetching from remote more than once; "git pull <URL> <branch>" (and its git-fetch + git-merge equivalent) is used for one-time pulls.Busy
I know this is an old post, but I have a question. if refspec = +refs/heads/master:refs/remotes/origin/master and git fetch master is not a fast-forward (let's say that people rebased the master branch in the remote repo), then will my local refs/remotes/master ref be reset to equal the same SHA1, or will some merge of the contents happen?Trifocals
@AlexanderBird: It is better to ask questions as questions, not in comments... Anyway, the ref will be reset, as there is nobody on the remote side that can be asked to resolve possible merge conflict - there is no person behind the screen.Busy
P
5

Note that the main maintainer for Git has now (Git 2.1, August 2014) added this explanation for git fetch:
(See commit fcb14b0 by Junio C Hamano (gitster):

CONFIGURED REMOTE-TRACKING BRANCHES

You often interact with the same remote repository by regularly and repeatedly fetching from it. In order to keep track of the progress of such a remote repository, git fetch allows you to configure remote.<repository>.fetch configuration variables.

Typically such a variable may look like this:

[remote "origin"]
fetch = +refs/heads/*:refs/remotes/origin/*

This configuration is used in two ways:

  • When git fetch is run without specifying what branches and/or tags to fetch on the command line, e.g. git fetch origin or git fetch, remote.<repository>.fetch values are used as the refspecs---they specify which refs to fetch and which local refs to update.
    The example above will fetch all branches that exist in the origin (i.e. any ref that matches the left-hand side of the value, refs/heads/*) and update the corresponding remote-tracking branches in the refs/remotes/origin/* hierarchy.

  • When git fetch is run with explicit branches and/or tags to fetch on the command line, e.g. git fetch origin master, the <refspec>s given on the command line determine what are to be fetched (e.g. master in the example, which is a short-hand for master:, which in turn means "fetch the 'master' branch but I do not explicitly say what remote-tracking branch to update with it from the command line"), and the example command will fetch only the 'master' branch.
    The remote.<repository>.fetch values determine which remote-tracking branch, if any, is updated.
    When used in this way, the remote.<repository>.fetch values do not have any effect in deciding what gets fetched (i.e. the values are not used as refspecs when the command-line lists refspecs); they are only used to decide where the refs that are fetched are stored by acting as a mapping.

Pennington answered 2/8, 2014 at 18:0 Comment(0)
P
2

Note also that, with Git 2.5+ (Q2 2015), git merge FETCH_HEAD can merge multiple git fetch's.

See commit d45366e by Junio C Hamano (gitster), 26 Mar 2015.
(Merged by Junio C Hamano -- gitster -- in commit bcd1ecd, 19 May 2015)

"git merge FETCH_HEAD" learned that the previous "git fetch" could be to create an Octopus merge, i.e. recording multiple branches that are not marked as "not-for-merge";
this allows us to lose an old style invocation "git merge <msg> HEAD $commits..." in the implementation of "git pull" script; the old style syntax can now be deprecated.

The git merge doc now mention:

When FETCH_HEAD (and no other commit) is specified, the branches recorded in the .git/FETCH_HEAD file by the previous invocation of git fetch for merging are merged to the current branch.


Git 2.13 (Q2 2017) officially retires the old syntax for git merge.
See commit b439165 (26 Mar 2015) by Junio C Hamano (gitster).
(Merged by Junio C Hamano -- gitster -- in commit 1fdbfc4, 30 Mar 2017)

merge: drop 'git merge <message> HEAD <commit>' syntax

Stop supporting "git merge <message> HEAD <commit>" syntax that has been deprecated since October 2007, and issues a deprecation warning message since v2.5.0.

That means the warning message old-style "'git merge <msg> HEAD <commit>' is deprecated." is no more.

Pennington answered 24/5, 2015 at 16:56 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.