56

My understanding is that the default branch of a cloned repository is to be whatever HEAD points to in the repo being cloned.

I now have a case where this is not true. My understanding is obviously flawed, so what does determine the default checkout branch when cloning a (bare) repo?

The last commit on that repo was a merge between the branch referenced in the bare repo's HEAD into the branch I'm getting as the checkout branch in the clone.

Running git remote show origin returns:

Fetch URL: ...
Push  URL: ...
HEAD branch (remote HEAD is ambiguous, may be one of the following):
  <bad-branch>
  live
Remote branches:
  ...

Bare repo uses Git version 1.8.2.1, client uses 1.7.12.4, transport is SSH.

Maybe the answer is actually this one here. This answer confirms it. If there is a choice of symbolic refs all pointing to the same revision as HEAD, the client will guess which branch to use.

Community
  • 1
  • 1
Christian Goetze
  • 2,254
  • 3
  • 34
  • 51
  • Can you set up a reproducer? What version(s) of git are involved (on the clone-ee and clone-er)? What transport is being used? – torek Sep 10 '13 at 18:37
  • Yes, it is happening to me as well. It seems that the client is choosing a branch that is pointing to the same commit that the remote's `HEAD` is pointing to. This happened after I removed the `master` branch and set another new branch to the be default. And at the moment there are some other branches also pointing to the latest commit of this new default branch. – Vicro Nov 13 '13 at 10:53

5 Answers5

35

Beginning with Git 1.8.5, the server will send the actual branch name that HEAD points to, in the "symref" capability. If you have both a client and server newer than Git 1.8.5, it will update HEAD correctly.

Prior to that, the client will guess what HEAD may have pointed to by comparing the object ID that HEAD (ultimately) points to with all the object IDs of all the branches. It prefers a branch named refs/heads/master: if both HEAD and master point to the same object ID, then clone will set the default branch in the new repository to master.

Otherwise, the first branch with a matching OID (when the branches are sorted alphanumerically) will be the default branch. If no branches have matching OIDs, then HEAD will be set directly to the object ID (ie, a detached HEAD).

Edward Thomson
  • 74,857
  • 14
  • 158
  • 187
  • `Beginning with Git 1.8.5, the server will send the actual branch name that HEAD points to` -- I don't understand. My understanding is that HEAD is a reference to the last commit in the currently checked-out branch. This quote implies that HEAD is independent of a checked-out branch. So which branch will be used beginning with Git 1.8.5? – Rob Bednark Apr 18 '18 at 16:28
  • 4
    `HEAD` is actually not a pointer to a commit; it's a pointer to a _branch_. That's what determine what the "currently checked-out branch" actually is. So to determine the last commit, you first look at `HEAD` which points to (for instance) `master` and _master_ is actually pointing to a commit. – Edward Thomson Apr 19 '18 at 08:16
  • 2
    (Note that I should say, to be technically correct, that `HEAD` is not _usually_ a pointer to a commit. If you get into a "detached HEAD state", where you check out a commit directly and not a branch, then `HEAD` will actually point to a commit.) – Edward Thomson Apr 19 '18 at 08:17
  • 1
    This approach worked for me - I logged into the remote that held the Git repository and updated the HEAD file in the repository folder. I changed it from "ref: refs/heads/master" to "ref: refs/heads/develop". Then I did a new checkout in my local machine and the "develop" branch was selected by default after Git clone. – Harri Jun 20 '18 at 13:36
17

It is actually what HEAD points to. Use git symbolic-ref HEAD refs/heads/mybranch for setting HEAD. (source: http://feeding.cloud.geek.nz/posts/setting-default-git-branch-in-bare/ )

Valentin Lorentz
  • 9,556
  • 6
  • 47
  • 69
  • When I researched this, I stumbled over that site - and as I commented in the other answer, here's a case where it's not true. There must be more going on, because I'm definitely not getting the branch that is referenced in the bare repo's HEAD. – Christian Goetze Sep 10 '13 at 18:30
7

A bare repo has a HEAD, too. That's what you get when you clone it.

From the git clone documentation:

Clones a repository into a newly created directory, creates remote-tracking branches for each branch in the cloned repository (visible using git branch -r), and creates and checks out an initial branch that is forked from the cloned repository's currently active branch.

The bit about "currently active branch" is referring to the remote's HEAD revision.

If you want different behaviour, you can use --branch or -b:

--branch <name>
-b <name>
Instead of pointing the newly created HEAD to the branch pointed to by the cloned repository’s HEAD, point to <name> branch instead. In a non-bare repository, this is the branch that will be checked out. --branch can also take tags and detaches the HEAD at that commit in the resulting repository.

Carl Norum
  • 219,201
  • 40
  • 422
  • 469
  • 3
    My problem is that it appears to not be true. On the remote bare repo, HEAD contains "ref: refs/heads/live". When I clone, I get pointed to a different branch. Now, it turns out that the last commit on that branch was a merge from the "remote tracking branch 'origin/live'" into the branch. Something is just weird. – Christian Goetze Sep 10 '13 at 18:28
  • Without access to your repo, I can't comment further, but you really do get HEAD by default. – Carl Norum Sep 10 '13 at 18:33
4

It's origin/HEAD. However if you want the name like 'origin/master' you'd have to parse the file like this

cat .git/refs/remotes/origin/HEAD | awk '{ print $2 }'
Archimedes Trajano
  • 35,625
  • 19
  • 175
  • 265
  • the git dir is not always `.git` (e.g. in submodules), so you should use `rev-parse --git-dir` to find it – xeruf Feb 25 '21 at 09:39
2

If want to manually check out the default branch of the origin remote, this should work reliably:

git switch $(cat $(git rev-parse --git-dir)/refs/remotes/origin/HEAD | cut -d'/' -f4)
xeruf
  • 2,602
  • 1
  • 25
  • 48