views:

70

answers:

2

I have a remote tracking branch tracked locally in my local repository using 'git branch -b branch-name origin/branch-name'. My remote branch is test2/test2 (origin/branch-name) which is being tracked locally as test2.

The origin is also named test2. I haven't checked-out my local tracking branch test2.

When i do a 'git pull origin remote-branch:local-tracked-branch' i get this error

[test2]$ git pull test2 test2:test2 From /gitvobs/git_bare/test2 ! [rejected] test2 -> test2 (non fast forward)

Whereas when i checkout my local tracking branch test2 and do pull 'git pull origin local-tracked-branch' i don't get the error and i do a pull using 'git pull test2 test2'

From /gitvobs/git_bare/test2 * branch test2 -> FETCH_HEAD Auto-merging a.txt Automatic merge failed; fix conflicts and then commit the result.

i know that adding a + (git pull test2 +test2:test2) would help but it overwrites local changes.

So how do i know which of my local branches are created by me locally using 'git branch new-branch-name' or tracked locally from remote branches using git branch -b branch-name origin/branch-name'?

+1  A: 

Independently of the list of tracked branched (which you can see with git config -l), a "non-fast-forward" message means cannot merge the remote branch (i.e. the local copy of the fetched commit of the remote branch) in your branch because:

  • your branch has commits of its own
  • the remote branch has new commits since the last pull

so:

  --last pull
  |
  v
x-x-x-x-x <--test2
  \
   -y-y-y <-- test2/test2

Whereas this would have been a fast-forward merge

  --last pull
  |
  v
x-x <--test2
  \
   -y-y-y <-- test2/test2

So:

git checkout test2
git fetch test2 test2 
git merge test2/test2
#fix conflicts
git commit

And please, do call your remote repo any other name than test2. That is way too many test2 here ;)


Now for the list of remote branches tracked in your local repo:

git config --get-regexp branch..*
VonC
Thanks Von, git config --get-regexp branch was the exact thing i was looking for to get the list of remote branches tracked in my local repo. Chris's excellent explanation about branches and pull made me more clear on different branches. And sure i would rename the repositories other than test2 :D
Senthil A Kumar
+3  A: 

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
Chris Johnsen
Wow, this is a lot of information on pull :)My doubts are cleared now. So i need to checkout a branch and do the pull. Thanks a lot Chris. and thanks for shell scrips, these are really useful modules in my scripts. Appreciate your response.
Senthil A Kumar
Very interesting explanation. +1
VonC