8

Since short, GitHub proposes to use main instead of master for the default branch.

How can we get that name, for writing resilient Git aliases? How to get the default for the "master" branch in Git?

I've searched on SO, but none of the solutions in git - how to get default branch? does work reliably for me...

If I'm in branch feature, forked from develop, it will return me develop and not master (or main, from which develop is a fork)...

How to get that "master" branch name, then?

NOTE -- The question could be: why is git remote show origin not showing me master, but develop instead, as the HEAD branch?

user3341592
  • 1,419
  • 1
  • 17
  • 36
  • What is *the default branch*? If I have 2 gits at a host, one `/usr/bin/git` and the other `/usr/local/bin/git` and the former `/usr/bin/git init` creates repositories with `master` but the latter `/usr/local/bin/git init` creates repositories with `main` — which is *the default*? IMO there is no such thing but every repository has its own default which is not necessary either `master` or `main`; it could be anything, for example, `default` or `trunk`. – phd Jan 13 '21 at 13:54
  • Yes, sure, it can be whatever the repo owner has decided. The question is, here, how to programmatically find that default name, for writing resilient Git aliases. – user3341592 Jan 13 '21 at 14:32
  • Execute `git remote show origin`, and then look for the text with `HEAD branch: main` (main will obviously be replaced by whatever is the default branch when cloning). This is mentioned on the page you link to and should work. If it doesn't then perhaps you should ask about that instead. I say this because if you're not saying *why* it doesn't work for you or what differs in your context, this question will probably be closed as a duplicate of that very question, because you're most likely going to get the exact same answers here. Until you tell us what is different in your case. – Lasse V. Karlsen Jan 13 '21 at 15:05
  • @LasseV.Karlsen The default remote is also not necessary `origin` :-) – phd Jan 13 '21 at 15:36
  • @user3341592 If there is just one branch in a repo — it's the default. If there're many branches but there is only one remote — resolve the `HEAD` from the remote and map it back to the local branch. But if there're many branches and many remotes — then oops. All remotes and branches are equal and the concept of the default is in user's head (pun intended), there is no way to extract the knowledge programmatically. The only way I see is to have some reasonable defaults (`origin` and `master`) in your aliases/scripts but allow users to override with config or parameters. – phd Jan 13 '21 at 15:44
  • @LasseV.Karlsen and others: I'm OK with `origin` (I don't change that for now) and I don't use multiple remotes - yet! Though, on my real case, when looking at the output of `git remote show origin`, I do see `develop` as `HEAD branch` while it should output `master` (which is by the way well listed as one of the remote branches..). Why is it so? I don't understand at all... – user3341592 Jan 13 '21 at 16:44

3 Answers3

10

There are two problems with answering your question

You mention main vs master. Now, the first problem here is that there isn't really any default branch at the moment. There sort of is, but there's no guaranteed way to query it, because the "sort of" part is because there's no common agreement on this yet: not everyone has Git version 2.28 or later installed.

That said, running:

git config --get init.defaultBranch

may produce something in your own Git repository, and if it does and if your Git is 2.28 or later, your Git will use that as the default name for the unborn branch when running git init. (See also commit 8747ebb7cde9e90d20794c06e6806f75cd540142.)

Note that if someone runs:

git init --initial-branch=xyzzy

to create a new, empty repository, the name of the unborn branch in this new, empty repository will be xyzzy, regardless of whether there is an init.defaultBranch setting, and regardless of what might be in it. The --initial-branch option also first appears in Git 2.28.

HEAD

You also asked, regarding answers to git - how to get default branch?:

The question could be: why is git remote show origin not showing me master, but develop instead, as the HEAD branch?

The git remote show command, and the git ls-remote command (try it), work by calling up some other Git repository and getting information from it. Before we discuss this information, let's take a look at how HEAD works in your repository.

A Git repository is, at its heart, a collection of commits. Each commit is numbered with a unique hash ID. All Git systems everywhere use a particular algorithm—currently SHA-1, but there are plans to switch to SHA-256—to compute the hash ID of a commit based on the commit's content, so that all Git systems will come up with the same hash ID for the same commit.

This is how Git works in term of distributing commits. Each repository has a full collection of its own commits. When you cross-connect a pair of Git repositories, such as your own and one over on a system your Git calls origin, they exchange hash IDs. Since the hash IDs uniquely identify the commits, the sender can list a hash ID, and the receiver can immediately tell from that whether he has the commit, or needs the commit. Add in some optimizations based on the commit graph structure and we have most of what we need for the have/want protocol exchange.

Each Git, though, finds commits in its own repository using names: branch names, tag names, and other such names. These come with hash IDs, which find some particular commit. Those commits then find any earlier commits as well.

For the purpose of sending commits, the sending Git in a git fetch pair-up lists out his branch and tag and other such names, so that the receiving Git can figure out which commits to ask for, if any. You can list out your own names—all of them or some selected subset—using git for-each-ref. The default is to list all names that start with refs/.

There is one piece missing from the above picture, and that's the special name HEAD. This special name—it doesn't start with refs/ and Git uses it internally all over the place in various special ways—is normally what Git calls a symbolic ref, and we can read it with git symbolic-ref:

$ git symbolic-ref HEAD
refs/heads/master

This tells us that in this particular Git repository, the current branch is the one named master. (Branch names all start with refs/heads/; other names start with other refs/-based prefixes.) In a non-bare Git repository—the kind that we normally work with—this means someone has run git checkout master or git switch master.

When we use git ls-remote to connect to some other Git, their Git will run git for-each-ref for us, to list out their names. But they prefix their list with the value in HEAD, and if we add the --symref option,1 we get their HEAD in two ways:

ref: refs/heads/master  HEAD
72c4083ddf91b489b7b7b812df67ee8842177d98        HEAD
71ca53e8125e36efbda17293c50027d31681a41f        refs/heads/maint
72c4083ddf91b489b7b7b812df67ee8842177d98        refs/heads/master
a3ce27912f980d787926b113d17f1f532e50162a        refs/heads/next
[snip]

So this tells me that their Git repository has master as their HEAD. The first output line makes this clear directly; the next few lines show me that their HEAD and their refs/heads/master both refer to commit 72c4083ddf91b489b7b7b812df67ee8842177d98, which is less direct.2

The git remote show command has the ability to call up the other Git.3 This means they can have them list out which branch they have as their HEAD. When you see:

  HEAD branch: develop

this merely means that their Git has their HEAD attached to their branch name develop. If their repository is non-bare, this means they ran git checkout develop or git switch develop.

If they have a bare repository, they can still get the same effect, using git --work-tree=... checkout, or GIT_WORK_TREE=... git checkout, or by running git symbolic-ref directly. The branch name to which their HEAD is attached is not necessarily their master or their main, if they have one: they control which branch name, if any, is stored in their HEAD.

There is very little significance attached to this. If their HEAD names their develop, that just means that their HEAD is attached to their develop. Since their branch names are theirs—not yours—this shouldn't matter to you.

There is one place where it does matter, though. If you run:

git clone <url>

instead of, say:

git clone -b somebranch <url>

then you're asking your Git to call up their Git and ask them which branch name they recommend. When you supply -b branch, you're choosing the branch; when you don't, you're telling your Git to follow their recommendation. The recommended branch name is the one to which their HEAD is attached. So they—whoever they are, in control of this other Git repository—should use whatever means they have at their disposal to connect their HEAD to the branch name they personally recommend.

There's no need for you to follow their recommendations. Your clone has your branch names in it. You do not need to use the same names they are using. That's up to you. Most people do mostly use the same names, to make things easier to think about.


1This --symref option is not supported in older versions of Git. When a newer Git is talking to an old Git that can't do symbolic ref transfers, the newer Git has to guess about the older Git's HEAD. This is a somewhat ugly situation and we will just assume that you're using all-newer-Git-versions here.

2This shows you how older Gits can guess: if the hash ID from HEAD matches exactly one branch name, that tells us that this is the branch name. But what if the hash ID from HEAD matches two, or three, or more, branch name hash IDs? That's where the situation becomes ugly.

3In modern Git, that's the default for the way you are using git remote show. I'm not sure if this was ever not the default. The git remote command has a lot of sub-commands though, and many of them don't bother calling up some other Git.

torek
  • 448,244
  • 59
  • 642
  • 775
  • 2
    Note: with the recent `git config clone.defaultRemoteName`, not even "`origin`" is a safe default ([Git 2.30, Q1 2021](https://stackoverflow.com/a/64627822/6309)) – VonC Jan 13 '21 at 23:06
5

This doesn't answer the question itself, but pragmatically the core problem is determining whether a repo uses the main or master naming standard. As such, in most cases we can do this:

git branch | grep -o -m1 "\b\(master\|main\)\b"
  • -o — print only the match
  • -m1 — print only the first match
dwelle
  • 6,894
  • 2
  • 46
  • 70
  • 1
    Since I referenced your regex in a [discussion on this matter](https://github.com/Bash-it/bash-it/pull/1981), its only fit that I give it an up-vote - Thanks for sharing ! – David Farrell Oct 23 '21 at 18:13
  • 1
    I added it as an answer at , but this is basically what I do: define this as an alias, and make use of that alias elsewhere. I like not relying on there being a remote. – Amory Nov 23 '21 at 11:44
  • This is perfect! I agree, making this an alias and using it in any other command to refresh variable MAIN_BRANCH or whatever, is a great solution. – Gandalf Saxe Feb 28 '22 at 19:46
3

To get the default branch name for a git repository that uses Github, install the gh command, built by Github. Then run

cd agithubrepo/
gh repo view --json defaultBranchRef --jq '.defaultBranchRef.name'

Example output:

main
DTrejo
  • 1,309
  • 9
  • 16
  • 1
    I'm not sure why this has few upvotes. The concept of "default" branch really only exists in these external providers - not git itself (where it is just a default init branch, which may not be the same for a repo you've cloned from upstream). Therefore asking the provider's API is the most robust way, and if you already have their tool installed, IMO also the simplest. Thanks for the answer – afirth Aug 02 '22 at 09:34