Search code examples
gitgit-branchgit-clonegit-remote

Repo and its clone disagree about HEAD pointer


I’m trying to rename my master branch to 'liveBranch', create a new branch ('devBranch'), then clone the repo (call it repo A) in another folder on the same computer (call it repo B). But when I do so, if I do a git branch -a on repo B, it shows the HEAD of repo A to be pointing to 'devBranch' while git branch -a on repo A claims that 'liveBranch' is checked out.

Here are my exact steps (note: repoA is a non-empty directory):

cd path/to/repoA
git init
git add .
git commit
git branch -m master liveBranch
git branch devBranch
git clone path/to/repoA path/to/repoB 
cd path/to/repoB

Running git branch -a in repo B returns:

* devBranch
  remotes/origin/HEAD -> origin/devBranch
  remotes/origin/devBranch
  remotes/origin/liveBranch

While running git branch -a in repo A returns:

  devBranch
* liveBranch

I thought this might be because both the two branches are actually pointing to the same commit, so neither repo is technically wrong. So I made a commit on one of the branches in repo A to advance the branch and did a git pull in repo B, but the disconnect is still occurring (repo B and repo A disagree on what branch repo A has checked out).


Solution

  • There is something a bit wrong with your "exact steps", because if I begin by trying to reproduce the problem:

    cd path/to/repoA
    git init
    git add .
    git commit
    

    I get this on my system:

    $ cd /tmp; mkdir btest; cd btest
    $ mkdir repoA; cd repoA
    $ git init
    Initialized empty Git repository in /tmp/btest/repoA/.git/
    $ git add .
    $ git commit
    On branch master
    
    Initial commit
    
    nothing to commit
    $ 
    

    It seems as though perhaps you are doing your git init in a repository that already exists and has some commit(s), otherwise master would still be an unborn branch at this point. Anyway, so now I change your steps a bit:

    $ echo 'dummy repo for testing' > README
    $ git add .
    $ git commit -m initial
    [master (root-commit) 82f36fb] initial
     1 file changed, 1 insertion(+)
     create mode 100644 README
    $ git branch -m master liveBranch
    $ git branch devBranch
    $ git branch
      devBranch
    * liveBranch
    $ 
    

    Now let's try cloning this to /tmp/btest/repoB:

    $ git clone /tmp/btest/repoA /tmp/btest/repoB
    Cloning into '/tmp/btest/repoB'...
    done.
    $ cd ../repoB
    $ git status
    On branch liveBranch
    Your branch is up-to-date with 'origin/liveBranch'.
    nothing to commit, working directory clean
    $ 
    

    and it is working the way you would like.

    Let's take a different approach to repeating the problem, beginning with removing both test repositories, then making a fresh repository with HEAD pointing to devBranch, then cloning that repository:

    $ cd /tmp/btest
    $ rm -rf *
    $ mkdir repoA; cd repoA; git init
    Initialized empty Git repository in /tmp/btest/repoA/.git/
    $ echo > README; git add README; git commit -m initial
    [master (root-commit) 8278cc4] initial
     1 file changed, 1 insertion(+)
     create mode 100644 README
    $ git branch -m master devBranch
    $ cd ..; git clone repoA repoB; (cd repoB; git status; git branch -A)
    Cloning into 'repoB'...
    done.
    On branch devBranch
    Your branch is up-to-date with 'origin/devBranch'.
    nothing to commit, working directory clean
    * devBranch
      remotes/origin/HEAD -> origin/devBranch
      remotes/origin/devBranch
    $ 
    

    So we have repoB in a suitable state. Now we alter repoA so that it has HEAD pointing to liveBranch:

    $ (cd repoA; git checkout -b liveBranch; git branch)
    Switched to a new branch 'liveBranch'
      devBranch
    * liveBranch
    $ 
    

    If we ask git to git pull in repoB, what should we now expect to happen? Well, let's see what does happen (note, this is with Git version 2.8.1; pre-1.8.4 behavior would be a bit different in some cases):

    $ cd repoB; git pull
    From /tmp/btest/repoA
     * [new branch]      liveBranch -> origin/liveBranch
    Already up-to-date.
    $ git branch -a
    * devBranch
      remotes/origin/HEAD -> origin/devBranch
      remotes/origin/devBranch
      remotes/origin/liveBranch
    $ 
    

    Now let's try something different, namely, running git fetch in repoB, and also git ls-remote:

    $ git fetch
    $ git branch -a
    * devBranch
      remotes/origin/HEAD -> origin/devBranch
      remotes/origin/devBranch
      remotes/origin/liveBranch
    $ git ls-remote
    From /tmp/btest/repoA
    8278cc44d45cad50f34dc2c788cd9df7bf9375ec    HEAD
    8278cc44d45cad50f34dc2c788cd9df7bf9375ec    refs/heads/devBranch
    8278cc44d45cad50f34dc2c788cd9df7bf9375ec    refs/heads/liveBranch
    

    Clearly neither git pull nor git fetch is reading the new remote HEAD state, or if it is, is falling back to name-to-ID translation. Let's update repoA with a new commit and re-fetch:

    $ (cd ../repoA; git commit -m update --allow-empty)
    [liveBranch 2234cf1] update
    $ git fetch
    remote: Counting objects: 1, done.
    remote: Total 1 (delta 0), reused 0 (delta 0)
    Unpacking objects: 100% (1/1), done.
    From /tmp/btest/repoA
       8278cc4..2234cf1  liveBranch -> origin/liveBranch
    $ git branch -a
    * devBranch
      remotes/origin/HEAD -> origin/devBranch
      remotes/origin/devBranch
      remotes/origin/liveBranch
    $ git ls-remote
    From /tmp/btest/repoA
    2234cf14c9f7c63785e8fe31b7e5f37bcaf51823    HEAD
    8278cc44d45cad50f34dc2c788cd9df7bf9375ec    refs/heads/devBranch
    2234cf14c9f7c63785e8fe31b7e5f37bcaf51823    refs/heads/liveBranch
    $ 
    

    So, yes, Git simply fails to update remotes/origin/HEAD after the initial clone, at least when using absolute paths. Changing the URL to file:///tmp/btest/repoA makes no difference:

    $ git config remote.origin.url file:///tmp/btest/repoA
    $ git fetch
    $ git branch -a
    * devBranch
      remotes/origin/HEAD -> origin/devBranch
      remotes/origin/devBranch
      remotes/origin/liveBranch
    

    and a quick look at the source code suggest that after the initial clone step, git never bothers to update remotes/origin/HEAD.