129

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?

Jeffrey Yasskin
  • 4,423
  • 1
  • 26
  • 36
lfender6445
  • 29,844
  • 11
  • 111
  • 95
  • 17
    The default branch is a github thing, not a git thing. – Ismail Badawi Feb 23 '15 at 03:10
  • You can use the GitHub API, as in this question: http://stackoverflow.com/questions/16500461/how-do-i-find-the-default-branch-for-a-repository-using-the-github-v3-api – Ismail Badawi Feb 23 '15 at 03:15
  • 21
    @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? – bluenote10 Jul 16 '19 at 07: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? – user3341592 Jan 12 '21 at 16:02
  • Question asked in https://stackoverflow.com/questions/65703168/how-to-get-the-default-for-the-master-branch-in-git – user3341592 Jan 13 '21 at 13:42

16 Answers16

117

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)

Radon8472
  • 3,493
  • 1
  • 27
  • 38
  • 17
    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/.*: //'` – JHH Oct 09 '18 at 12:01
  • 5
    Best method so far. I don't even have refs/remotes/origin/HEAD for some reasons. – Loïc Faure-Lacroix Oct 11 '18 at 16:08
  • It depends how you names your remote, or if you have added a remote. Try to run `git remote` and see what it shows – Radon8472 Oct 15 '18 at 14:18
  • @JHH You can also execute 'xargs' to get rid of spaces. That is: `git remote show [your_remote] | grep "HEAD branch" | cut -d ":" -f 2 | xargs` – balaganAtomi Aug 27 '19 at 12:54
  • 5
    I updated it to be like this to remove the space `git remote show origin | grep 'HEAD branch' | cut -d' ' -f5` – Andrew Sep 20 '19 at 16:56
  • 2
    Be aware that this might not work for some (older) versions of git, provided you have ambiguous HEAD. See e.g. [this post](https://stackoverflow.com/questions/2832269/git-remote-head-is-ambiguous) – David Střelák Sep 27 '19 at 09:53
  • It also doesn't work with localized (translated) Git. For example, in mine the output of `git remote show upstream` has `branch HEAD:`. – flod Oct 10 '20 at 05:26
  • Does not work reliably for me: if I'm in branch `feature`, forked from `develop`, it will return me `develop` and not `master` (whose `develop` is a fork)... Any help? – user3341592 Jan 11 '21 at 17:59
  • Other options to strip out the space; * sed-based: `git remote show origin | sed -e '/HEAD branch: /!d' -e '/HEAD branch: /s/.*: //'` * perl-based: `git remote show origin | perl -ne 'if (/HEAD branch:\s*(.*)/) { print "$1"} '` – bernard paulus Jun 11 '21 at 15:49
  • Using regex (js in my case), this pattern may be used: `result.match(/(?<=HEAD branch:).+/)?.[0].trim()` – Henrique Bruno Jul 25 '21 at 17:59
  • 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. – Hi-Angel Aug 08 '21 at 14:30
  • This works with Azure DevOps also. I found awk simpler for the filtering, ie. `git remote show origin | awk -F': ' '/HEAD branch/ { print $2 }'` – Ed Randall Dec 03 '21 at 10:46
  • 1
    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'` – Slaven Rezic Mar 23 '22 at 16:15
  • @Slaven I AM running git under multiple german, and I never hat such issues, but thx for adding more informations to the command. Which git version are you using that show this different translated name? – Radon8472 Mar 24 '22 at 22:49
  • 1
    @Radon8472 CentOS 7 has git version 1.8.3.1 and the wording is "Hauptzweig". Debian 9 has 2.11.0 and the wording here is "Hauptbranch". Later Debians and Ubuntu also have "Hauptbranch". However Ubuntu 18.04 with git 2.17.1 has "HEAD branch". So maybe it does not seem to depend on the git version. (All of these tried with `LC_ALL=de_DE.utf8 git remote show origin`) – Slaven Rezic Mar 27 '22 at 11:00
  • This is similar but seems to be much faster (a few ms vs a few seconds): `git branch -a | sed -n '/HEAD ->/s/.*\///p'`. You also don't need to know the name of the remote this way. Though it might not work if you have multiple remotes set up – redbmk May 18 '22 at 17:36
107

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
danielkza
  • 2,085
  • 1
  • 20
  • 20
  • 7
    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? – nhed Apr 05 '18 at 15:20
  • 3
    This works fine for origin, but when I tried getting the default branch for another remote, such as upstream in a forked github setup, I get "not a symbolic ref". I'm not git-savvy enough to understand why, but apparently ref/remotes/upstream won't exist at all. Radon8472's solution based on git remote worked for me though. – JHH Oct 09 '18 at 11:49
  • 17
    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. – christianbundy Nov 02 '18 at 17:03
  • 17
    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. – Diomidis Spinellis Jan 25 '19 at 15:20
  • 9
    You can also use **basename** instead of sed `basename $(git symbolic-ref refs/remotes/origin/HEAD)` – OzzyCzech Mar 26 '19 at 11:20
  • 1
    @lfender6445 the command from this answer returns incorrect results for my repos. The other one with git remote show ... returns the correct branch. – Side S. Fresh Sep 20 '19 at 07:08
  • 16
    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. – Edward Anderson Jan 14 '20 at 14:46
  • I've just cloned [AlfrescoLabs/chef-alfresco](https://github.com/AlfrescoLabs/chef-alfresco), run this command, and correctly received `develop` as the master branch name. – vault Nov 25 '21 at 12:24
  • This can fail if you don't have any remotes. If you're running it on the primary git repo, you have no remotes (you are the remote). In this case, just use "HEAD" – macetw Mar 25 '22 at 17:59
34

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.

Jeffrey Yasskin
  • 4,423
  • 1
  • 26
  • 36
  • 3
    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-`. – scottclowe Jun 15 '21 at 00:20
  • Isn't this actually the canonical answer, since it uses a plumbing command and doesn't require any utilities outside of git itself? – Andrew Spencer Jun 18 '21 at 11:47
  • 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. – Andrew Spencer Jun 18 '21 at 11:52
  • (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) – Andrew Spencer Jun 18 '21 at 11:54
  • 2
    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. – Hi-Angel Aug 08 '21 at 14:42
  • Hello, I recently created an alias based on this: https://stackoverflow.com/a/69950967/10095231 – kelvin Nov 15 '21 at 21:30
25

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.

JoeLinux
  • 3,980
  • 28
  • 31
  • A possible PowerShell solution: `(git remote show origin | Select-String "HEAD branch: " -Raw).Split(' ', [System.StringSplitOptions]::RemoveEmptyEntries)[2]` – kapsiR May 05 '21 at 07:04
  • 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` – fredericrous Jul 07 '21 at 21:57
  • 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. – Hi-Angel Aug 08 '21 at 14:35
24

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 git@github.com:pre-commit/pre-commit.github.io HEAD
ref: refs/heads/real_master HEAD
e100a6a3c72b4e54f0d176f791dfd2dbd7eb5fa7    HEAD
Anthony Sottile
  • 49,611
  • 12
  • 110
  • 158
  • 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}'` – hnakamur Jul 27 '20 at 09:02
11

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

$ basename $(git symbolic-ref --short refs/remotes/origin/HEAD) 
master
Louis
  • 4,262
  • 3
  • 38
  • 53
Christian Hesse
  • 273
  • 2
  • 3
  • 12
    For remote it would still be `git symbolic-ref --short refs/remotes/origin/HEAD | sed 's@^origin/@@'` – maxm Oct 04 '18 at 22:28
  • 4
    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)` – dannysauer Jul 29 '20 at 21:09
  • 1
    basename does not work for all branch names as they can contain slashes – Anthony Sottile Jan 23 '22 at 17:18
8

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

Henry
  • 427
  • 6
  • 14
  • 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! – rattray May 27 '21 at 18:39
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

Henrik N
  • 14,896
  • 4
  • 75
  • 122
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
Carl Walsh
  • 5,201
  • 2
  • 41
  • 41
5

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

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

pfesenmeier
  • 51
  • 1
  • 1
5

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.

Amory
  • 606
  • 2
  • 19
  • 26
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
    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 :) ) – shalomb Apr 10 '19 at 10:28
3

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
Makusu2
  • 31
  • 2
2

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

$ cat .git/refs/remotes/origin/HEAD 
ref: refs/remotes/origin/master
anorm
  • 2,209
  • 1
  • 18
  • 37
1

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.

VonC
  • 1,129,465
  • 480
  • 4,036
  • 4,755
0

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.

Kankaristo
  • 1,875
  • 2
  • 16
  • 16