Understanding .git/config's 'remote' and 'branch' sections
TL;DR summary
Overall, we're interested in two or three things:
- If you run
git fetch
with no additional arguments, what happens? - If you run
git merge
orgit rebase
with no additional arguments, what happens? - If you run
git pull
with no additional arguments, what happens?
The short answer to question #1 is: Git picks one of your remotes to fetch from, and then fetches from that remote. The remote Git picks is from the remote = name
setting under the [branch]
section. If there isn't any such setting, Git uses origin
.
The answer to question #2 is: Git picks some name to use, as if you had run git merge name
or git rebase name
. The name
is based on the merge = ref
setting under the [branch]
section—but the way this works is somewhat obscure: if this same section says, for instance, remote = origin
and branch = refs/heads/master
, the name Git picks to use is not master
but rather origin/master
. If it says remote = origin
and branch = develop
, the name Git picks is origin/develop
, and so on.
(While this looks very straightforward, the actual mapping inside Git is rather tricky: if the section says remote = .
and branch = master
, the name is master
, not ./master
, for instance. If you set up unusual fetch refspecs, even more weirdness can happen. This answer does not cover this last case at all.)
The answer to question #3 is in some ways the easiest: git pull
simply runs git fetch
first, then—provided that succeeds—one of the other two commands, git merge
or git rebase
, so you really only have to look at questions 1 and 2.
Long
The merge
entry under each branch
section is, I think, the least obvious. The Git documentation keeps it a bit obscure. Let's cover the others first.
Settings under a [remote "..."]
section
There are many possible settings. In general, you do not have to set any of them with git config
directly—almost all of them have wrapper commands to set them in a more "user friendly" manner. That includes both of the settings you see here. It's rare to want to change these, either.
The remote
section for each named remote, such as origin
, lists the URL for git fetch
(and optionally a separate push URL for git push
, and other remote.*
configuration items as described in the git config
documentation). It also has one or more fetch
lines which supply the default refspec arguments for git fetch
from that remote.
That is, if you run:
git fetch origin
Git will look up remote.origin.url
to see where to connect, then connect there, then retrieve references based on all the remote.origin.fetch
entries. The default that you see here:
+refs/heads/*:refs/remotes/origin/*
tells Git to copy all branches1 from the remote, renaming them to an origin/
-prefixed remote-tracking branch2 in your own repository, so:
git fetch origin
basically fetches everything. (The leading +
says that Git should do this regardless of whether the remote-tracking branch update is a fast-forward operation. That is, it's like using --force
, but without having to specify --force
.)
On the other hand, if you run:
git fetch origin a:b c:d
Git will completely ignore all the fetch =
lines, retrieving only references a
and c
from the remote, writing them to references b
and d
in your repository. (And since this has neither +
nor --force
, none of these will be force-updated—though in most cases that makes no difference anyway.)
1, 2 A reference is a generic term that covers both branches and tags (and more things as well). Branch names like master
are just short-hand for references that begin with refs/heads/
. Remote-tracking branch names like origin/master
are just short-hand for references that begin with refs/remotes/
. Note that the origin/
part comes from the fetch =
line—but for all this to work the way it is supposed to, that line must match the name of the remote in the square brackets.
Settings under a [branch "..."]
section
There are many possible settings. In general, you do not have to set any of them with git config
directly—almost all of them have wrapper commands to set them in a more "user friendly" manner. That includes both of the settings you see here. It's not that rare to want to change one or both of them, using a command we will see in a moment.
The remote
part is pretty clear on its own, though: it means that if you are on branch master
and run git fetch
without giving a remote name at all, Git should fetch from the remote named origin
.
The merge
part is the tricky one. It lists the name of a branch as seen on the remote. Note that when we run git fetch origin
, we tell our Git to call up another Git, find the other Git's master
, and copy that into our repository but call it origin/master
. And yet ... this merge
line says merge = refs/heads/master
. Shouldn't it say: merge = refs/remotes/origin/master
?
It probably should—but this setting predates the very invention of remotes in the first place. So it doesn't; instead, it lists the full name of the reference as it appears on the remote.
This setting is what gets used if you run git merge
or git rebase
without providing a branch name to merge or rebase-upon. Git runs the name through the mappings provided by the fetch =
line for the remote, to figure out that it should merge with origin/master
, for instance.
This setting is also used by the git pull
convenience command, which is effectively3 the same as running git fetch
followed by running git merge
.
You might want to change one or both of these. For instance, if you create a new local branch feature/tall
, it may have no branch.feature/tall.remote
and branch.feature/tall.merge
settings at all.
Since you just created this branch, there is no origin/feature/tall
. The Git over at origin
does not have feature/tall
yet, so you do not have a copy of it.
Then you git push origin feature/tall:feature/tall
to have your Git call up origin
's Git and have their Git create that branch, so that you now do have origin/feature/tall
. You might want your Git to remember that.
You could run two git config
commands, but instead, you can run one higher level wrapper command:
git branch --set-upstream-to=origin/feature/tall feature/tall
This tells your Git to set branch.feature/tall.remote
to origin
, and branch.feature/tall.merge
to refs/heads/feature/tall
(that being the name on origin
).
You can combine the git push
and the git branch --set-upstream-to
steps using git push -u
, which is even better, but the point here remains: you use a wrapper to get both values set at once, since setting only one value is not that useful.4
The special remote name .
means this repository (as opposed to some remote repository). If the [branch "xyzzy"]
section says remote = .
and branch = refs/heads/whatever
, then branch xyzzy
has local branch whatever
as its upstream, rather than having, e.g., origin/whatever
as its upstream.
3This deliberately glosses over a lot of fiddly details.
4Setting just the remote
part does affect a future git push
, but git merge
and git rebase
won't be able to do the remote-tracking branch mapping without both entries.
Its called refspec. Its the mechmism that git is using to "talk" to the remote server and to map local branches to remote branches.
Refspecs
A refspec maps a branch in the local repository to a branch in a remote repository.
This makes it possible to manage remote branches using local Git commands and to configure some advanced git push and git fetch behavior.
A refspec is specified as [+]<src>:<dst>
. The <src>
parameter is the source branch in the local repository, and the <dst>
parameter is the destination branch in the remote repository.
The optional +
sign is for forcing the remote repository to perform a non-fast-forward update.
Refspecs can be used with the git push command to give a different name to the remote branch. For example, the following command pushes the master branch to the origin remote repo like an ordinary git push, but it uses qa-master as the name for the branch in the origin repo. This is useful for QA teams that need to push their own branches to a remote repo.
git push origin master:refs/heads/qa-master
By adding a few lines to the Git configuration file, you can use refspecs to alter the behavior of git fetch.
By default, git fetch
fetches all of the branches in the remote repository. The reason for this is the following section of the .git/config
file:
[remote "origin"]
url = https://[email protected]:mary/example-repo.git
fetch = +refs/heads/*:refs/remotes/origin/*
The fetch
line tells git fetch to download all of the branches from the origin repo.
But, some workflows don’t need all of them. For example, many continuous integration workflows only care about the master branch. To fetch only the master branch, change the fetch line to match the following:
[remote "origin"]
url = https://[email protected]:mary/example-repo.git
fetch = +refs/heads/master:refs/remotes/origin/master
You can also configure git push in a similar manner. For instance, if you want to always push the master branch to qa-master in the origin remote (as we did above), you would change the config file to:
[remote "origin"]
url = https://[email protected]:mary/example-repo.git
fetch = +refs/heads/master:refs/remotes/origin/master
push = refs/heads/master:refs/heads/qa-master
Refspecs give you complete control over how various Git commands transfer branches between repositories.
They let you rename and delete branches from your local repository, fetch/push
to branches with different names, and configure git push and git fetch to work with only the branches that you want.