68

I can find the current git branch name by doing either of these:

git branch | awk '/^\*/ { print $2 }'
git describe --contains --all HEAD

But when in a detached HEAD state, such as in the post build phase in a Jenkins maven build (or in a Travis git fetch), these commands doesn't work.

My current working solution is this:

git show-ref | grep $(git log --pretty=%h -1) | sed 's|.*/\(.*\)|\1|' | sort -u | grep -v HEAD

It displays any branch name that has the last commit on its HEAD tip. This works fine, but I feel that someone with stronger git-fu might have a prettier solution?

neu242
  • 15,796
  • 20
  • 79
  • 114
  • 10
    “current git branch […] in detached HEAD” – Detached HEAD means that there is no current branch, so what branch are you trying to find? – poke May 19 '11 at 17:20
  • 8
    @poke: The OP's example makes it pretty clear what he's looking for. – Cascabel May 20 '11 at 12:09
  • @Jefromi: I know, but it's not a good idea to ask for something and define that by an already working solution. Not everyone is able to read that to see what kind of result comes out.. – poke May 20 '11 at 12:19
  • 3
    @poke: I'm not just asking for a working solution, I'm asking for the _simplest way_ (or "git way"?) of finding the matching branch name(s). – neu242 May 23 '11 at 06:35

8 Answers8

60

A more porcelain way:

git log -n 1 --pretty=%d HEAD

# or equivalently:
git show -s --pretty=%d HEAD

The refs will be listed in the format (HEAD, master) - you'll have to parse it a little bit if you intend to use this in scripts rather than for human consumption.

You could also implement it yourself a little more cleanly:

git for-each-ref --format='%(objectname) %(refname:short)' refs/heads | awk "/^$(git rev-parse HEAD)/ {print \$2}"

with the benefit of getting the candidate refs on separate lines, with no extra characters.

Cascabel
  • 479,068
  • 72
  • 370
  • 318
  • 10
    You can use `--pretty=%D` to get `HEAD, branchname` without the surrounding parens. Tested on git v2.11.0 – thom_nic Nov 07 '17 at 21:26
  • make sure you are looking at the origin references on jenkins and not the local references as pointed out by @epeli – Jilles van Gurp Dec 14 '17 at 11:08
  • 3
    `git show -s --pretty=%D HEAD | tr -s ', ' '\n' | grep -v HEAD | head -n1` – jpbochi Mar 07 '19 at 15:16
  • update: `git show -s --pretty=%D HEAD | tr -s ',' '\n' | sed 's/^ //' | grep -v -e HEAD -e '^origin/' | head -n1` Only works if detached, unfortunately. – jpbochi Mar 07 '19 at 16:08
  • $ mkdir test $ cd test $ git init . $ touch a.txt $ vi a.txt $ git add a.txt $ git commit -am 'a' $ git status $ git checkout -b test1 $ git status $ git branch master * test1 $ git show -s --pretty=%d HEAD (HEAD -> test1, master) $ vi a.txt $ git commit -am '2' $ git status $ git show -s --pretty=%d HEAD (HEAD -> test1) $ git checkout master $ git show -s --pretty=%d HEAD (HEAD -> master) $ git merge test1 $ git show -s --pretty=%d HEAD (HEAD -> master, test1) – user1766151 Aug 04 '19 at 06:40
  • Another option that works whether HEAD is detached or not `git show -s --pretty=%D HEAD | cut -f2 -d ',' | xargs`. That `| xargs` at the end trims whitespace. – Daniel Oct 05 '21 at 00:45
  • if HEAD is tagged this for me showed the tag name instead of branch name – grenix Oct 12 '22 at 14:40
43

I needed a bit different solution for Jenkins because it does not have local copies of the branches. So the current commit must be matched against the remote branches:

git ls-remote --heads origin | grep $(git rev-parse HEAD) | cut -d / -f 3

or without network:

git branch --remote --verbose --no-abbrev --contains | sed -rne 's/^[^\/]*\/([^\ ]+).*$/\1/p'

It's also worth noting that this might return multiple branch names when you have multiple branch heads at the same commit.

UPDATE:

I just noticed that Jenkins sets GIT_BRANCH environment variable which contains a value like origin/master. This can be used to get git branch in Jenksin too:

echo $GIT_BRANCH | cut -d / -f 2
javabrett
  • 7,020
  • 4
  • 51
  • 73
esamatti
  • 18,293
  • 11
  • 75
  • 82
18
git branch --contains HEAD

Obviously discarding (no branch). Of course, you may get an arbitrary number of branches which could describe the current HEAD (including of course none depending on how you got onto no-branch) which might have be fast-forward merged into the local branch (one of many good reasons why you should always use git merge --no-ff).

Seth Robertson
  • 30,608
  • 7
  • 64
  • 57
  • @neu242: Which command from the other answer returns a useful branch (eg not HEAD) where this command will not (if it is a remote branch add -a)? It works fine during test `git checkout @{0}` – Seth Robertson May 23 '11 at 12:30
  • 1
    All of @Jefromi's answers, plus my "current working solution" returns useful branch(es) in a detached HEAD state. – neu242 May 24 '11 at 06:24
  • Unlike the accepted answer this shows branch name and not tag name if HEAD is tagged. However I just suppose it could report multiple brach names. Is that true? – grenix Oct 12 '22 at 14:43
  • Oh stupid me you already mentioned that multiple brach names thing, just with other words – grenix Oct 12 '22 at 14:56
4

A sed solution:

git log -1 --pretty=%D HEAD | sed 's/.*origin\///g;s/, .*//g'

This uses log to check the last item for its presence on a branch. Then sed finds the branch preceded by origin/ and removes the phrase and everything before it. Then sed does another removal of any possible additional listed branches (comma and everything after it). The reason last log was used as a sanity check to ensure this detached HEAD is not a commit above known branch HEADs.

If this is empty, failsafe logic can be implemented to label the branch "detached" (or "undefined"?) or to ensure it's up-to-date or rolled back to the tip of a known HEAD.

ingyhere
  • 11,818
  • 3
  • 38
  • 52
  • 1
    Works great, thanks! :-) Using alternate separators in the sed expression does not require escaping `/` and might be easier to read, e.g. `... sed 's|.*origin/||g;s|, .*||g'`. – ssc Oct 16 '21 at 10:10
3

I prefer to use this one:

git branch --remote --contains | sed "s|[[:space:]]*origin/||"

It works fine if the head of a branch is checked out as well as when the current checkout is a detached head and it requires no network access.

Mecki
  • 125,244
  • 33
  • 244
  • 253
  • just keep in mind that "origin" is just a standard convention and no git keyword. There might be repos that dont use "origin" at all. Anyhow on my repo it worked. – grenix Oct 12 '22 at 15:09
2

Here's git nthlastcheckout, it gets the exact string you used for your nth last checkout from the reflog:

git config --global alias.nthlastcheckout '!nthlastcheckout'"() {
        git reflog |
        awk '\$3==\"checkout:\" {++n}
             n=='\${1-1}' {print \$NF; exit}
             END {exit n!='\${1-1}'}'
}; nthlastcheckout \"\$@\""

Examples:

$ git nthlastcheckout
master
$ git nthlastcheckout 2
v1.3.0^2
jthill
  • 55,082
  • 5
  • 77
  • 137
  • This is showing only a commit hash for me, but thanks for the idea for using reflog. Maybe reflog is not plumbing enough to be parseable? – grenix Oct 12 '22 at 14:52
2

The shortest I got working on bitbucket pipelines:

git show -s --pretty=%D HEAD | awk '{gsub("origin/", ""); print $2}'
Ruslanas
  • 61
  • 6
0

This is what I am using in one of my projects:

const { execSync } = require('child_process');

const getBranchName = () => {
  let branch = execSync('git rev-parse --abbrev-ref HEAD').toString().trim();
  if (branch === 'HEAD') branch = execSync(`git branch -a --contains HEAD | sed -n 2p | awk '{ printf $1 }'`).toString().trim();
  return branch;
}
Raphael Setin
  • 557
  • 5
  • 10
  • For noobs like me you could mention what progr. language this is. Anyhow: Why not always use the method with 'git branch -a --contains HEAD' ? – grenix Oct 12 '22 at 15:08
  • @grenix sorry, I wrote that in "automatic mode". That was using JavaScript in a NodeJS environment. If you have more questions, please let me know. – Raphael Setin Oct 12 '22 at 19:54