Are the names of a remote-tracking branch and the corresponding remote branch being tracked necessarily the same?
If they can have different names, how does
git fetch
match the two branches then? (a typicalrefspec
togit fetch
is+refs/heads/*:refs/remotes/remote/*
)If I am correct, given a remote-tracking branch, we can create a local-tracking branch that associates with it but has a different branch name. (by the
-b
option ingit checkout
)Further if the names of the remote-tracking branch and the corresponding remote branch being tracked are the same, how does
git push
match the local-tracking branch and the remote branch? (a typicalrefspec
togit push
is+refs/heads/*:refs/heads/*
)

- 1
- 141
- 372
- 590
-
http://stackoverflow.com/questions/21839651/git-what-is-the-difference-between-push-default-matching-and-simple – Andrew C Dec 31 '15 at 21:56
4 Answers
Are the names of a remote-tracking branch and the corresponding remote branch being tracked necessarily the same?
No. However, making them not-match results in a lot of pain (and I haven't tested it on the push side).
If they can have different names, how does git fetch match the two branches then? (a typical refspec to
git fetch
is+refs/heads/*:refs/remotes/remote/*
)
You may have multiple fetch =
lines, so you could, for instance, do this:
[remote "strange"]
fetch = +refs/heads/master:refs/remotes/strange/surprise
fetch = +refs/heads/other:refs/remotes/strange/surprise2
Note, however, that you can no longer use refs/heads/*
on the left of any additional fetch refspecs, since that will match master
and other
and (presumably) map them to names other than surprise
and surprise2
, and git fetch
aborts with an error message in this case. This effectively forces you to list every refs/heads
name that you wish to copy from the given remote (strange
, in this case).
(As I said, I haven't tested this with push and I don't know if push obeys the same mapping rules as fetch. There were some recent changes, around git 2.5 or so, to handle "triangular" work-flows better, where you fetch from localmirror
and push to centralserver
for instance. One of those changes was to add the same kind of name-mapping for the push remote. Presumably, before this new code went in, doing this kind of pushing brought even more pain, possibly even without a triangular work-flow; and presumably now it works better....)
We might call this "silly renaming tricks", and my advice would be: don't use them. :-) I think they'll work correctly with most commands, and fail with others, though I can't point to any specific failure examples (just vague memories of how I used to do things).
If I am correct, given a remote-tracking branch, we can create a local-tracking branch that associates with it but has a different branch name. (by the -b option in git checkout)
Yes; and this works fine for all kinds of local work. Again, I'd avoid the phrase "local-tracking branch", and just say "local branch with an upstream" as this is the direction git documentation has moved since about 1.7 (see below).
Remember that "the upstream of local branch $branch
" is produced by:
- obtaining the remote name from
git config --get branch.$branch.remote
, and - mapping branch-name from
git config --get branch.$branch.merge
through thefetch =
refspecs for that remote.
Thus, suppose we've created two local branches test1
and test2
and have the following:
$ git config --get branch.test1.remote
origin
$ git config --get branch.test1.merge
refs/heads/test
$ git config --get branch.test2.remote
origin
$ git config --get branch.test2.merge
refs/heads/test
Both test1
and test2
refer to refs/heads/test
, which is the name of a branch on the other git repository that will be located via the name origin
: this is why we need to run these through the fetch =
map(s) for origin
.
In the absence of silly renaming tricks, the "mapped through" part leaves the branch-name part (everything after refs/heads
) unchanged, and just replaces the middle bits, so that refs/heads/test
becomes refs/remotes/origin/test
. That's really easy to make assumptions about. I believe some lazy script-writers (including myself in the past) may have used this bit of shell script code:
fullbranch=$(git rev-parse --symbolic-full-name $branch) || exit 1
remote=$(git config --get branch.$branch.remote)
rmtbranch=refs/remotes/$remote/$branch
which not only assumes the lack of silly renaming tricks, it even assumes that if we're on branch test1
, the upstream must be origin/test1
, not origin/test
. Slightly less lazy script writers (including myself in the past) then had to fix their scripts, e.g.:
fullbranch=$(git rev-parse --symbolic-full-name $branch) || exit 1
remote=$(git config --get branch.$branch.remote)
theirname=$(git config --get branch.$branch.merge)
rmtbranch=refs/remotes/$remote/${theirname#refs/heads/}
which now assumes that refs/heads/test
on the origin
maps to refs/remotes/origin/test
in the local repository.
Adding silly renaming tricks means that we can't find the actual upstream name easily at all, but various commands (e.g., git merge
, git rebase
) automatically find the correct upstream. To make it easier for scripting, git version 1.7.0 grew the @{upstream}
notation: you can now simply write $branch@{upstream}
. The git parser looks up the upstream for you, and the above (broken) script fragment can be rewritten as:
rmtbranch=$(git rev-parse --symbolic-full-name $branch@{upstream}) || exit 1
This is all fine and good for fetch, but what about push? Well, if you're pushing to the same remote that you're fetching from, you just do the same thing. But you can, for any number of reasons,1 split them up: fetch from repository F
and push to repository P
. In this case, we might need different mappings for F
and P
.
Git 2.5 introduced @{push}
, as noted by VonC in some earlier SO thread I can't find offhand, and in this github blog posting. The new @{push}
notation simply switches from using the fetch upstream to using the push upstream, i.e., using the P
mapping instead of the F
mapping.
There's still one good question, and it's your last one here:
Further if the names of the remote-tracking branch and the corresponding remote branch being tracked are the same, how does git push match the local-tracking branch and the remote branch? (a typical refspec to git push is
+refs/heads/*:refs/heads/*
)
My answer for git 2.5, with the new push stuff, is: I don't really know for sure, but your "typical refspec" is no longer the default (since git 2.0). When you run git push
with no refspec arguments, git looks up your push.default
setting (along with a bunch more optional settings but push.default
is effectively2 required). It has five possible values, and only one of them—not the default—amounts to refs/heads/*:refs/heads/*
.
One of those settings is upstream
, and that setting runs the current branch's merge
setting through the map functions (presumably, in git 2.5 and later, through the new separate push map if present, else through the fetch map).
1One reason is to use a pull-request repository, as in the linked github blog post. Another is to fetch from a local mirror (as at a large corporate setup with local mirrors for various branch offices), but push to a single central server (the corporate-designated "master" site that all the local mirrors actually mirror).
2If you fail to set push.default
, git spits out a lot of annoying text. The best way to shut it up is to set push.default
, so that means you're "required" (in some sense anyway) to set it. And, since git 2.0, the default setting, if you haven't set it, is simple
, which forbids silly renaming tricks.

- 448,244
- 59
- 642
- 775
-
Thanks. Your reply to "Are the names of a remote-tracking branch and the corresponding remote branch being tracked necessarily the same?" is no. If I am correct, remote tracking branches are created by cloning, or fetching/pulling, or pushing, how can you specify the remote tracking branches to be created with names different from the remote branches being tracked? Or change the names of remote-tracking branches after creating them? – Tim Jan 01 '16 at 07:10
-
See my upcoming answer to [your question about when remote-tracking branches get created](http://stackoverflow.com/q/34538343/1256452). – torek Jan 01 '16 at 08:59
refspec
As you said its the refspec
which lies inside your .git/config file.
The git config file contains the information about the refspec
Note
In order to see what you described do this:
// Checkout master
git checkout master
// rename the master branch to a new name
git branch -m <old name> <new name>
// View the .git/config file and you will see the the new branch is still
// pointing (tracking) to the master branch, just like you described
Using the refspec
you can control to which branch pull/push
will refer.
Seting tracking branch
git branch -u <upstream>/<branch name>
matching
Git v2.0 Release Notes
Backward compatibility notes
When git push [$there]
does not say what to push, we have used the
traditional "matching" semantics so far (all your branches were sent
to the remote as long as there already are branches of the same name
over there). In Git 2.0, the default is now the "simple" semantics,
which pushes:
only the current branch to the branch with the same name, and only when the current branch is set to integrate with that remote branch, if you are pushing to the same remote as you fetch from; or
only the current branch to the branch with the same name, if you are pushing to a remote that is not where you usually fetch from.
You can use the configuration variable push.default
to change this.
If you are an old-timer who wants to keep using the
matching
semantics, you can set the variable to "matching", for
example. Read the documentation for other possibilities.

- 1
- 1

- 128,036
- 21
- 144
- 167
-
Thanks. But I am not sure how your reply addresses the issue of the corresponding branches not having the same name. – Tim Dec 31 '15 at 23:04
-
Read the section with the notes. It can have different names as long as you defined the tracking branch. (this is the refspec under you .git/config), Correct me if i did not undersood you corrdctly – CodeWizard Dec 31 '15 at 23:06
-
(1) Do you mean when the corresponding branches don't have the same name, just specify them explicitly rather than using `/*` in `refspec`? (2) Does the "simple" semantics apply only to `git push`? Is there alternative semantics for `git fetch/pull`, other than the "matching" semantics? – Tim Dec 31 '15 at 23:36
The answer from @codeWizard is the full and complete answer, but the plain english answer (based on your comment questions on his answer) is:
1 is really two questions. The answer to the first is: No, they don't have to have the same name. The answer to the second is: fetch
doesn't actually map the local and remote branches. All fetch
does (using your example--the defaults) is ask remote for it's list of refs/heads/*
, and the associated blobs, and stores the refs in refs/remotes/*
rooted using the remote name.
2 The mapping of the local branch to the remote branch is controlled by either using explicit pull
and fetch
calls, or setting everything up in .git/config
. In git/config
The pull behavior is controlled by $branch.merge
, but push is controlled by push.default
: which by default will push to the same branch name on the remote as the local. If you set this to upstream
then it'll use the $branch.merge
as the push target.
Walkthrough:
mkdir t1 && cd t1
git init
touch file
git add file
git commit -a -m "remote"
cd ..
git clone t1 t2
cd t2
git branch -a # you should see 'master' and 'origin/master'
git branch -m master local
git pull # Should say "Already up-to-date"
git branch -a # you should now see 'local' and 'origin/master'
git push # damn! It pushed 'local' to 'origin/local'!
git push origin :local # delete 'origin/local'
git config push.default upstream # tell git to use 'local.merge' as the push target
git push # Now it should say "Everything up-to-date"
And after all this your t1
config should look like this:
[core]
repositoryformatversion = 0
filemode = true
bare = false
logallrefupdates = true
And t2
should look like this:
[core]
repositoryformatversion = 0
filemode = true
bare = false
logallrefupdates = true
[remote "origin"]
url = /nas/src/t/t1
fetch = +refs/heads/*:refs/remotes/origin/*
[branch "local"]
remote = origin
merge = refs/heads/master
[push]
default = upstream

- 1,875
- 1
- 16
- 30
-
Thanks. Does `git branch -m master local` not change `merge = refs/heads/master` in `t2/.git/config` to `merge = refs/heads/local`? If not, can the subsequent `git pull ` work properly? – Tim Jan 08 '19 at 03:48
-
Although the default behavior around some of this has changed since the original post: `git branch -m master local` still creates `merge = refs/heads/master` because this is referring to where on the _remote_ that branch's data lives. If you changed it to `refs/heads/local` it'd look for `t1:refs/heads/local` and wouldn't find it so pulls would break. – Guildencrantz Jan 09 '19 at 18:05
I know it's an old question, but clear-cut answer is right from man page of git-config (enter git help config
or man git-config
). Examples on how to use below information is right after the quotation:
push.default
Defines the action git push should take if no refspec is explicitly given. Different values are well-suited for specific workflows; for instance, in a purely central
workflow (i.e. the fetch source is equal to the push destination), upstream is probably what you want. Possible values are:
· nothing - do not push anything (error out) unless a refspec is explicitly given. This is primarily meant for people who want to avoid mistakes by always being
explicit.
· current - push the current branch to update a branch with the same name on the receiving end. Works in both central and non-central workflows.
· upstream - push the current branch back to the branch whose changes are usually integrated into the current branch (which is called @{upstream}). This mode only
makes sense if you are pushing to the same repository you would normally pull from (i.e. central workflow).
· simple - in centralized workflow, work like upstream with an added safety to refuse to push if the upstream branch’s name is different from the local one.
When pushing to a remote that is different from the remote you normally pull from, work as current. This is the safest option and is suited for beginners.
This mode has become the default in Git 2.0.
· matching - push all branches having the same name on both ends. This makes the repository you are pushing to remember the set of branches that will be pushed out
(e.g. if you always push maint and master there and no other branches, the repository you push to will have these two branches, and your local maint and master
will be pushed there).
To use this mode effectively, you have to make sure all the branches you would push out are ready to be pushed out before running git push, as the whole point of
this mode is to allow you to push all of the branches in one go. If you usually finish work on only one branch and push out the result, while other branches are
unfinished, this mode is not for you. Also this mode is not suitable for pushing into a shared central repository, as other people may add new branches there, or
update the tip of existing branches outside your control.
This used to be the default, but not since Git 2.0 (simple is the new default).
So, to set git to whichever configuration that suits you, enter:
git config push.default nothing
replace nothing
with your preferred configuration. If you want it to be global (all repos on your local machine), add the global option:
git config --global push.default nothing
To answer your questions:
- No
- Yes

- 3,416
- 2
- 34
- 37