237

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?

7
  • 28
    The default branch is a github thing, not a git thing. Commented Feb 23, 2015 at 3:10
  • You can use the GitHub API, as in this question: stackoverflow.com/questions/16500461/… Commented Feb 23, 2015 at 3:15
  • 44
    @IsmailBadawi Really? When creating a local bare repo and performing a clone on that there must still be some logic that determines which branch is checked out by default, right? Commented Jul 16, 2019 at 7:33
  • None of the below solution 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)... Any help? Commented Jan 12, 2021 at 16:02
  • Question asked in stackoverflow.com/questions/65703168/… Commented Jan 13, 2021 at 13:42

22 Answers 22

197

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)

Sign up to request clarification or add additional context in comments.

21 Comments

This worked well for me, except that the cut command leaves a space before the actual branch name, which can cause problems when using this from scripts. I ended up using git remote show upstream | grep "HEAD branch" | sed 's/.*: //'
Best method so far. I don't even have refs/remotes/origin/HEAD for some reasons.
I updated it to be like this to remove the space git remote show origin | grep 'HEAD branch' | cut -d' ' -f5
Be aware that this might not work for some (older) versions of git, provided you have ambiguous HEAD. See e.g. this post
This won't work if git is running under an non-English locale. For example with a German locale HEAD branch is spelled Hauptbranch. To fix this use: LC_ALL=C git remote show origin | sed -n '/HEAD branch/s/.*: //p'
|
186

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/@@'

Sample output:

master

Or:

git symbolic-ref refs/remotes/origin/HEAD --short

Sample output:

origin/master

11 Comments

if i change the default branch on the server side (github) this still gets the old default in an old but otherwise current clone (but fresh clones are fine). How does one force an update here?
Maybe I'm doing something wrong, but when I run this I get: fatal: ref refs/remotes/origin/HEAD is not a symbolic ref with Git 2.19.1.
This method can fail or return incorrect results. The ref may be a hash that cannot be resolved to a branch rather than a symbolic reference. This can be solved by looking at HEAD as well. Still, in 6528 repositories I checked the two git symbolic-ref methods return wrong results (e.g. master rather than develop for Alfresco/chef-alfresco) in 172 cases. The git remote show method proposed by @Radon8472 is more reliable and seems to return the correct result in a few of the 172 diverging cases I verified by hand.
You can also use basename instead of sed basename $(git symbolic-ref refs/remotes/origin/HEAD)
To sync this symbolic ref from upstream, git remote set-head origin --auto. This updates both what is seen in git remote show and the symbolic ref referenced here.
|
96

You can use git rev-parse.

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.

8 Comments

This runs a lot faster than git remote show, because it uses local information without polling the remote. You can trim off the first 7 characters (i.e. "origin/") and get just the branch name with git rev-parse --abbrev-ref origin/HEAD | cut -c8-.
Isn't this actually the canonical answer, since it uses a plumbing command and doesn't require any utilities outside of git itself?
Answering myself - this is NOT the canonical answer since it only works in a repo created by cloning from the remote - not from a repo that you created and then added the remote by git remote add and pushed to it.
(still upvoting since it's the best answer in the likely use case of a script where you know that you've cloned the repo from a remote)
Doesn't work for me: when executed as in the answer, the only stdout I get is origin/HEAD, and there are also errors warning: ignoring dangling symref refs/remotes/origin/HEAD, fatal: ambiguous argument 'origin/HEAD': unknown revision or path not in the working tree.. If I append to the command a -- ., it says warning: ignoring dangling symref refs/remotes/origin/HEAD, fatal: bad revision 'origin/HEAD'. However, the git symbolic-ref answer this answer refers to works for me.
|
39

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

6 Comments

Thanks for a nice answer. I added awk to take the branch name: git ls-remote --symref https://github.com/hnakamur/ltsvlog HEAD | awk '/^ref:/ {sub(/refs\/heads\//, "", $2); print $2}'
Thats a nice answer, but it does only give you the current HEAD of the remote, and it gives no information which one is the default-branch.
real_master here is the default branch
@Radon8472 On GitHub, isn't the HEAD on a remote repo the default branch?
@MartinBraun no its not. Many bare remotes don`t have a head. And when you e.g. use a remote for deploy, the head can be whatever the user wanted. So Head has nothing to do with default branch, HEAD tells you what point of history you currently have checked out. You can even have a detached head, what is not even linked to any branch.
|
30

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.

7 Comments

A possible PowerShell solution: (git remote show origin | Select-String "HEAD branch: " -Raw).Split(' ', [System.StringSplitOptions]::RemoveEmptyEntries)[2]
git remote show is slow because it queries the server. Using the -n option it's faster but only gives back the list of branches already known. That might not be the best solution but one could write the following: git remote show origin -n | grep -c main &> /dev/null && echo main || echo master
Doesn't work for me, it says "Connection timed out". I guess that's because the command accesses the network instead of using the locally available info, I think it's worth mentioning in the post.
This will fail if git is localized. See also stackoverflow.com/questions/28666357/…
This works great with git 2.37.1!
|
28

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

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

9 Comments

For remote it would still be git symbolic-ref --short refs/remotes/origin/HEAD | sed 's@^origin/@@'
It's a path, so in most POSIX-y shells you can also just use basename to get the last component. :D basename $(git symbolic-ref --short refs/remotes/origin/HEAD)
basename does not work for all branch names as they can contain slashes
it does not work $ git symbolic-ref --short refs/remotes/origin/HEAD fatal: ref refs/remotes/origin/HEAD is not a symbolic ref ;$ git --version git version 2.36.1
Gives only fatal: ref refs/remotes/origin/HEAD is not a symbolic ref for me
|
21

Requirements

You need refs/remotes/origin/HEAD and it is only set when cloning. Otherwise you need to set it yourself with

git remote set-head origin -a

More info

Command

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).

If your shell does not have the rev command, you can use awk instead:

git branch -rl '*/HEAD' | awk -F/ '{print $NF}'

If your shell is bash or bash-compatible, you can use variable string operations (thanks @qneill):

(out=$(git branch -rl '*/HEAD'); echo ${out##*/})

(The outer parentheses prevent a variable -- out in this example -- from being created or overwritten in the current shell session.)

7 Comments

I am working somewhere that stage is used as trunk for development, and master is used for deployment. This gives me stage, which is what I wanted – thanks!
Gives for me bash: rev: command not found and fatal: -a and -r options to 'git branch' do not make sense with a branch name
@Radon8472 If your shell does not have the rev command, you can use awk instead: git branch -rl '*/HEAD' | awk -F/ '{print $NF}'
@Henry this gives fatal: -a and -r options to 'git branch' do not make sense with a branch name
@qneill Not all POSIX systems with git have bash. All POSIX systems have awk.
|
20

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

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

Comments

16

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.

8 Comments

But your way does not realy check what is assignes as default-branch in remote. What if the defaultbrauch is called e.g. my-default or anytrhing other?
In my second paragraph, I detail one of the main reasons I find this useful: repositories without a remote.
Yes, I was reading this, but the question above asked how to get it from remote repo. So you answer is informativ, but it is no answer to the question that was asked here
The whole "main" change was made by people who had no idea how much it would break.
“That's good. Most of the answers here rely on checking a remote, but that relies on there actually being a remote,[…]”—That’s what OP asked for. “Is there a git command available to determine default branch for remote repository?”
|
9

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

This list the default local repository branch:

git rev-parse --abbrev-ref $(git remote)/HEAD

The GitHub API can show the default branch with using its CLI gh:

gh api /repos/{owner}/{repo} --jq '.default_branch'

Comments

8

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

2 Comments

What are the potential drawbacks of altering the local origin/HEAD ref?
@ChrisLong Basically none, but it could confuse scripts or someone could have a specific reason to want to keep their origin/HEAD at some outdated value.
6

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

Comments

5

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

Comments

4

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

1 Comment

It's worth pointing out .git/refs/remotes/origin/HEAD will not exist only if you've cloned the repo not if you've created/authored it. git symbolic-ref refs/remotes/origin/HEAD refs/remotes/origin/master can be used to link refs ( but that defeats the point of this question :) )
4

From git 2.28.0 and above:

$ git config --get init.defaultBranch || echo master

7 Comments

This works well for me git config --get init.defaultBranch
It only shows what will be default branch when create git repo. Use this instead to get default branch for current repo stackoverflow.com/a/67170894/588759
git var GIT_DEFAULT_BRANCH is simpler.
@Guildenstern this gives the first branch created by git init, not what is the "default" branch on a remote.
@bric3 The answer I replied to is about init.defaultBranch, not whatever GitHub does. I don’t know why you are replying to me.
|
3

Adding some context about remote names to the excellent answers of danielkza or Jerrey Yasskin.

So the example show it's possible to get the default branch of the remote named origin.

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

But if the remote are not named origin this could fail


$ git remote show
bric3
upstream

$ git rev-parse --abbrev-ref origin/HEAD
origin/HEAD
fatal: ambiguous argument 'origin/HEAD': unknown revision or path not in the working tree.
Use '--' to separate paths from revisions, like this:
'git <command> [<revision>...] -- [<file>...]'

Also not all remote have a default branch

$ git rev-parse --abbrev-ref bric3/HEAD
bric3/HEAD
fatal: ambiguous argument 'bric3/HEAD': unknown revision or path not in the working tree.
Use '--' to separate paths from revisions, like this:
'git <command> [<revision>...] -- [<file>...]'

$ ls .git/refs/remotes/bric3/HEAD
".git/refs/remotes/bric3/HEAD": No such file or directory (os error 2)

$ git rev-parse --abbrev-ref upstream/HEAD
upstream/master

$ ls .git/refs/remotes/upstream/HEAD
.git/refs/remotes/upstream/HEAD

Comments

1

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.

Comments

1

Most of the answers are based on git remote show, which is very slow for me (almost a full second on a repo with only a few branches). In the comments, using git symbolic-ref was mentioned. This is much faster (feels instant).

If that matters to you, here is a crappy benchmark that runs 50 iterations each:

  • git remote show origin | sed -n '/HEAD branch/s/.*: //p'
  • git symbolic-ref refs/remotes/origin/HEAD | sed 's/.*\///'
echo -ne "\nremote show:"
time for i in {1..50}; do git remote show origin | sed -n '/HEAD branch/s/.*: //p' >/dev/null; done
echo -ne "\nsymbolic-ref:"
time for i in {1..50}; do git symbolic-ref refs/remotes/origin/HEAD | sed 's/.*\///' >/dev/null; done

Output running on a crappy Chromebook VM:

remote show:
real    0m32.495s
user    0m1.566s
sys 0m0.811s

symbolic-ref:
real    0m0.207s
user    0m0.115s
sys 0m0.218s

Update: I think a clearer/faster version is: cat .git/refs/remotes/origin/HEAD | sed 's/.*\///' (but this only works from the root of the repo, whereas the other versions work from any directory inside the repo)

Comments

0

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

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

Comments

0

In Powershell to get the main branch remotely (without cloning anything locally) I just use this line

$remoteMasterBranchName = $(Split-Path ([Regex]::Matches((git ls-remote --symref $repository),"ref:\s+(\S+)\s+HEAD").Groups[1].Value) -Leaf)

If you need to convert it to another scripting language it essentially does 3 things:

  1. Executes the command git ls-remote --symref <repository clone url>
  2. Extracts the first match of the output with the regex "ref:\s+(\S+)\s+HEAD", e.g. lines like the one below (/s matches any whitespace character, /S matches any non-whitespace character):

    ref: refs/heads/master HEAD

    and brings out the central part refs/heads/master into the first regex group [(\S+) in regexp, Groups[1].Value in powershell]:
  3. Takes out the last part of the path refs/heads/master using Split-Path cmdlet, e.g. master which is the default branch we are looking for.

Comments

0

Here's the straight-forward, easy to do (and correct) method to both set and check your default branch in Git.

To set the default branch to whatever you want when you do a git init enter the following:

~$ git config set --global init.defaultBranch master

To check what your default branch is going to be whenever you initialize a git repo, enter the following:

~$ git config list

[email protected]
user.name=joeuser
init.defaultbranch=master

Comments

-2
> git var GIT_DEFAULT_BRANCH
master

From the docs for git var:

GIT_DEFAULT_BRANCH: The name of the first branch created in newly initialized repositories.

This variable was added in git 2.35.

1 Comment

This shows your local config, affecting new repos you'll git init in future. It shows me same value even if I chdir into directories where the remote defaults to main vs master vs gh-pages.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.