git pull Confusion
Over-specificity
Your git pull command includes too much information.
[test2]$ git pull test2 test2:test2
From /gitvobs/git_bare/test2
! [rejected] test2 -> test2 (non fast forward)
i know that adding a + (git pull test2 +test2:test2) would help but it overwrites local changes.
This is what your command means:
# *------------ (1) remote repository name
# / *------- (2) ref in remote repository
# / / *-- (3) ref in local repository
# / / /
git pull test2 test2:test2
# Means this: From remote repository `test2` (1),
# fetch branch `test2` (2), store it in local branch `test2` (3), then
# merge the fetched history into HEAD.
You are telling git pull to overwrite your local test2
branch with whatever the remote has on its test2
branch and then merge that with HEAD. You probably do not want to include the destination part of the refspec (the :test2
).
If the local branch you have checked out is configured to track something (see the “Branches: …” below), just do
git pull
If you need to supply (or override) a remote and repository, just supply the remote name/url and the local branch on the remote (leave off the final part of the refspec):
git pull test2 test2
Pull Into a Branch That is Not Checked Out
git pull is (as mentioned above) a combination of git fetch and git merge (or git rebase).
In general, a merge might involve conflict resolution. Conflict resolution requires a working tree. Therefore, it is not possible to perform a normal merge operation without a working tree. This means that your current HEAD has to be one of parents of the merge (it will be the first parent). Doing a rebase also needs a working tree for conflict resolution.
Since a pull involves a merge or rebase, it is not possible to pull into a local branch that is not checked out. You can only pull into the currently checked out branch.
Branches: Local, Tracking, Remote Tracking
The various types of Git branches are all the same underlying object: refs. Refs live in the refs/
namespace in $GIT_DIR/refs/
and $GIT_DIR/packed-refs
.
- “Local” branches live in the
refs/heads/
namespace.
- To examine the
test2
local branch ref:
git show-ref refs/heads/test2
, or
cat .git/refs/heads/test2
, or
grep -F refs/heads/test2 .git/packed-refs
- “Remote Tracking” branches live in the
refs/remotes/<remote-name>/
namespaces.
- Remote tracking branches are local copies of branches from a remote repository.
- The name “remote tracking” makes sense when you think of it like this, but it can be confused with the unfortunately named
--track
functionality of git branch and git checkout (see the final branch type).
- To examine the
test2
remote tracking branch ref:
git show-ref refs/remotes/test2/test2
, or
cat .git/refs/remotes/test2/test2
, or
grep -F refs/remotes/test2/test2 .git/packed-refs
Local branches that track another branch are normal local branches (in refs/heads/
) that have extra configuration in $GIT_DIR/config
:
[branch "test2"]
remote = test2
merge = refs/heads/test2
It is important to note that the merge
(or rebase
) configuration option names a ref on the remote. So refs/heads/test2
here means the local branch test2
found on the remote test2
. The special remote name .
can be used to refer to local branches in the local repository.
- The purpose of local branches that “track” some other branch is to make it easy to just type
git pull
and have it merge in (or rebase on top of) the history in some other branch.
You said that you wanted to distinguish plain plain local branches from local branches that track some other branch. You can do this by looking for the branch configuration in the $GIT_DIR/config
file.
You might use git config to do this:
branch_tracks_something() {
{
git config branch."$1".merge ||
git config branch."$1".rebase
} >/dev/null 2>&1
}
# test local branch
branch_tracks_something test2 && echo 'test2 tracks something' || echo 'test2 does not track anything'
Or, if you have Git 1.6.3 or later, you can use the %(upstream)
format of git for-each-ref:
{ echo 'show_ref_desc() {
case "$1" in
refs/heads/*)
t=''
test -n "$2" && t=" (tracks $2)"
echo "local: $1$t"
;;
refs/remotes/*)
echo "remote tracking: $1"
;;
*)
echo "other: $1"
;;
esac
}'; git for-each-ref --shell --format='show_ref_desc %(refname) %(upstream)'; } |
sh
The output looks like this:
local: refs/heads/test2 (tracks refs/remotes/test2/test2)
remote tracking: refs/remotes/test2/HEAD
remote tracking: refs/remotes/test2/test2