'git - how to get default branch?

My team alternates between usage of dev and master as default branch for several repos and I would like to write a script that checks for the default branch when entering a directory.

When pull requests are opened in some of these repos, they either default to 'dev' or 'master' as the merge target.

I understand how to set this information but not retrieve it: https://help.github.com/articles/setting-the-default-branch/

Is there a git command available to determine default branch for remote repository?



Solution 1:[1]

Tested with git 2.9.4 (but possibly works in other versions) in a repo cloned from Github:

$ git symbolic-ref refs/remotes/origin/HEAD | sed 's@^refs/remotes/origin/@@'
master

Solution 2:[2]

I found a way to detect the default-branch if it is not master.

git remote show [your_remote] | sed -n '/HEAD branch/s/.*: //p'

I tested it with multiple repo from gitlab, and it worked fine. (for the most situations [your_remote] will be origin, run git remote to check the name of your remote)

Solution 3:[3]

git rev-parse --abbrev-ref origin/HEAD will print origin/<default-branch-name>. The git symbolic-ref answers are doing the same thing but need a longer argument.

If the origin repository changes its default branch name, then git remote set-head origin -a will retrieve the new default branch name.

Solution 4:[4]

This question is a bit old but in case anyone comes across this more recently...

git remote show <remote_name> | awk '/HEAD branch/ {print $NF}'

That will also only display the branch name, not including any of the whitespace or other nonsense.

I like to save this using a couple git aliases (I have a bunch of useful aliases like this):

upstream-name = !git remote | egrep -o '(upstream|origin)' | tail -1
head-branch = !git remote show $(git upstream-name) | awk '/HEAD branch/ {print $NF}'

I use "upstream" and "origin" as my remotes almost 100% of the time ("upstream" when I go with a Fork & Pull workflow... which is often). Your use case may not need the upstream-name alias, I just find it useful.

Solution 5:[5]

There doesn't seem to be an answer that doesn't require cloning so far

This requires git 2.8.0 or newer

$ git ls-remote --symref [email protected]:pre-commit/pre-commit.github.io HEAD
ref: refs/heads/real_master HEAD
e100a6a3c72b4e54f0d176f791dfd2dbd7eb5fa7    HEAD

Solution 6:[6]

There is a --short option to git symbolic-ref. So my preferred command:

$ basename $(git symbolic-ref --short refs/remotes/origin/HEAD) 
master

Solution 7:[7]

The following command will list the HEAD branch, no matter how you have named your remotes:

git branch --remotes --list '*/HEAD'

From that you can extract the default branch like this:

git branch -rl '*/HEAD' | rev | cut -d/ -f1 | rev

(using short variants of the git branch arguments).

Solution 8:[8]

I just wanted a shell script to know whether the branch is "master" or "main".

For that purpose, this seems good enough:

[ -f "$(git rev-parse --show-toplevel)/.git/refs/heads/master" ] && echo master || echo main

If you know it will always be called from the root directory of the repo, it can be simplified to

[ -f .git/refs/heads/master ] && echo master || echo main

I'm using this in my Git aliases like so: https://github.com/henrik/dotfiles/commit/6815bd95770afab2936fb6202b1ee5e82cb9662b

Solution 9:[9]

If like this question you are trying to get a GitHub default branch--not some other git server:

You can get the default branch using the /repos GitHub API. It's the default_branch field of the response:

$ curl -s https://api.github.com/repos/darthwalsh/bootstrappingCIL | \
      jq --raw-output .default_branch
master

Solution 10:[10]

This is possible to get with the gh cli tool (tested v2.0.0)

gh repo view --json defaultBranchRef --jq .defaultBranchRef.name

Solution 11:[11]

As noted by other answerers, the concept of a default branch is a GitHub thing doesn't really make sense (in the lofty sense) of Git (there's a reasonably good (if under-appreciated) comment here gets into it a bit: https://stackoverflow.com/a/65710958/2521092) but in practice, we all know what that means.

Since the original asking of this question, Git 2.28 added init.defaultBranch, allowing a different initial branch than master, and a lot of projects are now using main. That's good. Most of the answers here rely on checking a remote, but that relies on there actually being a remote, and that there exists a reliable and consistent naming scheme to those remotes. Those are probably (increasingly?) reasonable assumptions, but it's not a guarantee, and I don't find that any of the major answers here fail nicely.

Moreover, my main use case is using the name of the default branch for various git aliases (e.g. lm = log main..HEAD). I'd like to use the same aliases without thinking about it too much, regardless of an external repo using master or a local using main with no remote. Git and its config can't really "store" information, so there's no way to set ahead of time what the current repository's main branch is. As such, any alias that wants to, say, show commits between the main branch and HEAD can't assume git log master..HEAD or git log main..HEAD will work.

Thus, I define a default-branch alias in Git that figures out the default branch and then supply that to other aliases. This is a pain since a simple lm = log main..HEAD has to instead become lm = "!git log $(git default-branch)..HEAD" but here we are:

default-branch = "!git branch --sort=-refname | grep -o -m1 '\\b\\(main\\|master\\|dev\\)\\b'"

This simply gets the branch names then finds the first one in a defined list. If there's a main, use that; if not and there's a master, use that. I also have dev in there as a tertiary option some folks use.

This is sort of similar to what @henrik-n does in https://stackoverflow.com/a/66622363/2521092, but I do it in Git itself rather than the shell, and I think it has some more optionality.

Solution 12:[12]

This works for me with Git 2.1.10, using a repository cloned from GitHub:

git branch -r --points-at refs/remotes/origin/HEAD

A major problem with this approach is that it lists every remote branch pointing to HEAD; however, the output includes a hint:

  origin/HEAD -> origin/master
  origin/master
  origin/test123

So you can post-process the output with grep or similar to find the one with the arrow:

git branch -r --points-at refs/remotes/origin/HEAD | grep '\->' | cut -d' ' -f5 | cut -d/ -f2

Solution 13:[13]

All other answers made too many assumptions, this is the only way that worked for me. This works regardless of what branch you're currently on, doesn't assume the origin/HEAD ref exists locally, and will always reflect the current default branch even if it's been changed.

The only drawback is that it alters the local origin/HEAD ref, but that shouldn't generally be a problem.

Allow git to set your origin/HEAD, determining what branch to use automatically:

$ git remote set-head origin --auto
origin/HEAD set to main

Then, get a string containing the name of the default branch:

$ git rev-parse --abbrev-ref origin/HEAD
origin/main

Alternatively, for a one-line solution:

$ git remote set-head origin --auto >/dev/null 2>&1 && git rev-parse --abbrev-ref origin/HEAD
origin/main

Solution 14:[14]

Seems like a bit of a workaround solution but this seems to work:

$ cat .git/refs/remotes/origin/HEAD 
ref: refs/remotes/origin/master

Solution 15:[15]

Is there a git command available to determine default branch for remote repository?

No, there doesn't seem to be:

git ls-remote -h https://github.com/<user>/<repo>

That would list all branches, but not HEAD (which is the symref which designates the default branch)

Similarly, the GitHub Reference API can list heads, but would not include HEAD as well.

Solution 16:[16]

I'll add yet another answer, based on the other answers. I didn't like any of the other answers because querying a remote is slow, but I also didn't want a "local-only" solution.

I use this (long) alias:

head-branch = "!f() { gitHeadsDir=\"$(git rev-parse --show-toplevel)/.git/refs/heads\"; if [ -f \"$gitHeadsDir/main\" ]; then echo 'main'; elif [ -f \"$gitHeadsDir/master\" ]; then echo 'master'; else git remote show origin | grep 'HEAD branch' | cut -d' ' -f5; fi; }; f"

The above basically does this:

  • if a local branch named main exists, use that
  • if a local branch named master exists, use that
  • otherwise, fall back to checking the remote (which is much slower)

This works for 99 % of use cases (and all of my use cases), including:

  • a "regular" cloned repository
  • a brand new repository, which might not even have a remote (yet).

You can still easily "break" this by creating a local branch named main or master (or both), even if that's not actually the default branch. This will also fail if you don't have a remote, and your default branch name is neither main or master. But in those cases you're most likely trying to break it. ;-)

I'm checking for main first, since if you have both main and master, you're likely in the process of switching from master to main.