0

Creating a repo with a different default branch than master is easy with git init --initial-branch=main myRepo, or git config --global init.defaultBranch main, for instance (see How can I create a Git repository with the default branch name other than "master"? for details).

I want the inverse: when building git scripts that need the name of the default integration branch, is the local git metadata aware of what this branch name is, so it can tell me, and if so, how do we get at that data?

I thought something like git rev-parse --abbrev-ref origin/HEAD would work, but that just seems to show what the default branch was of the repo it was cloned from, at the time of cloning, provided that the remote we cloned from was named origin.

ecmanaut
  • 5,030
  • 2
  • 44
  • 66
  • 1
    Does this answer your question? [git - how to get default branch?](https://stackoverflow.com/questions/28666357/git-how-to-get-default-branch) – Dominik Nov 19 '20 at 02:14
  • Possible duplicate: https://stackoverflow.com/questions/28666357/git-how-to-get-default-branch – Dominik Nov 19 '20 at 02:16
  • Awesome – thought its "accepted" answer doesn't work under all circumstances, I found bits I can compose into a solution that seems to work well for all instances I have tried. I'll self-answer with what I came up with. – ecmanaut Nov 19 '20 at 02:27

2 Answers2

1

This seems to work well in all edge cases I've tried it with locally:

git remote show $(git remote show|tail -1)|grep 'HEAD branch'|awk '{print $NF}'

ecmanaut
  • 5,030
  • 2
  • 44
  • 66
1

The main problem here is that the problem itself is a little bit poorly defined.

There are many questions we can ask and answer about branch names. Each one is specific to one particular repository because all branch names are local to that particular repository. Any changes anyone makes in that repository affect only that one repository, at least at the time they make them.

Before we dive in any further, let's mention the special name HEAD. This name is truly very special: it's hardcoded into Git, and at least in current implementations, is implemented as a file named HEAD in the .git directory. If this file is missing, Git won't believe that the repository is a repository. What this HEAD is, though, is normally a symbolic ref, i.e., it contains a branch name, fully spelled out, such as refs/heads/master or refs/heads/main.

So, to define your question better, we'd like to know: What branch name is stored in the HEAD of a Git repository that we can contact by using a remote name $remote?

I thought something like git rev-parse --abbrev-ref origin/HEAD would work, but that just seems to show what the default branch was of the repo it was cloned from, at the time of cloning, provided that the remote we cloned from was named origin.

Correct on all counts: when we run:

git clone <url>

our Git uses the URL to call up some other Git. That other Git inspects some Git repository, which has branches. Their Git lists out all their branches, and also their HEAD. In a modern Git,1 their listing is able to provide the actual symbolic expansion, so that our Git can see that their HEAD contains refs/heads/name.

Our clone process renames all of their branch names to become our remote-tracking names instead, so that their develop becomes our origin/develop for instance (assuming the remote itself is named origin). So our Git will, at the end of the cloning process, have a remote-tracking name for each branch name. Our Git will then create our own symbolic reference, refs/remotes/origin/HEAD, that contains the adjusted name, such as refs/remotes/origin/main or whatever.

When we have our git rev-parse examine our Git repository to view our origin/HEAD, what we see is whatever we have stored in this origin/HEAD. That need not match what is in their HEAD at this time. It might match! It might not.

There are several things we can do about this:

  • We can contact their Git right now and view their HEAD symbol. In a sufficiently modern Git, that includes getting the symbolic expansion of their HEAD:

     $ git ls-remote --symref origin HEAD
     ref: refs/heads/master  HEAD
     faefdd61ec7c7f6f3c8c9907891465ac9a2a1475        HEAD
    
  • We can run git remote show, as in your own answer.

  • We can run git remote set-head remote --auto. This updates our own origin/HEAD right now to match whatever they have right now.

Which of these to use depends on the result you want. Note that by the time you get the answer, it may be incorrect (out of date). There is no way to fix this locally. Using some ESP,2 imagine the remote you're contacting is in orbit around Saturn. It takes light about 8 minutes to travel from the sun to Earth, and about 80 to travel from the sun to Saturn, so depending on where we are orbitally, they're 72 to 88 minutes away. Any answer you get back from them will necessarily be over an hour out of date.

(Using git remote set-head has the advantage of updating a cached answer, which you can then use for some set period. A direct query with git ls-remote has the advantage of getting a fresh answer and being fairly robust. The git remote show method seems like a compromise between these two that has most of their disadvantages with few of their advantages, so that's the one I would avoid.)


1Old Git versions lack this capability. They will simply show some raw commit hash ID for their HEAD. Our Git must guess which branch name, if any, goes with this.

2Exaggeration of System Parameters. I read about this concept decades ago and have not been able to find a proper source for attribution, so, no link here. It's a great way to test various limits. When you think about this even more, it's a little mind-bending, as we're trying to impose a global clock ("who is the most up to date") on a system that inherently doesn't have a global clock. When we scale time down to nanoseconds, this affects us in the real world of today: a light-nanosecond is not very far.

torek
  • 448,244
  • 59
  • 642
  • 775
  • 1
    This is a terrific answer! Without something like locks or transactions, we indeed will only ever be able to get an updated-as-of-when-the-repository-just-told-us point of accuracy that gets stale if changed in the time since then, but that is a resolution of reality constraint, and a lot likelier to be accurate than the clone-time default branch. – ecmanaut Dec 21 '20 at 21:54