125

I am new to shell scripting and can't figure this out. If you are unfamiliar, the command git branch returns something like

* develop
  master

, where the asterisk marks the currently checked out branch. When I run the following in the terminal:

git branch | grep "*"

I get:

* develop

as expected.

However, when I run

test=$(git branch | grep "*")

or

test=`git branch | grep "*"`

And then

echo $test

, the result is just a list of files in the directory. How do we make the value of test="* develop"?

Then the next step (once we get "* develop" into a variable called test), is to get the substring. Would that just be the following?

currentBranch=${test:2} 

I was playing around with that substring function and I got "bad substitution" errors a lot and don't know why.

Ben
  • 2,027
  • 3
  • 15
  • 13

6 Answers6

134

Expanding on Noufal Ibrahim's answer, use the --short flag with git-symbolic-ref, no need to fuss with sed.

I've been using something like this in hooks and it works well:

#!/bin/bash

branch=$(git symbolic-ref --short HEAD)

echo
echo "**** Running post-commit hook from branch $branch"
echo

That outputs "**** Running post-commit hook from branch master"

Note that git-symbolic-ref only works if you're in a repository. Luckily .git/HEAD, as a leftover from Git's early days, contains the same symbolic ref. If you want to get the active branch of several git repositories, without traversing directories, you could use a bash one-liner like this:

for repo in */.git; do branch=$(cat $repo/HEAD); echo ${repo%/.git} :  ${branch##*/}; done

Which outputs something like:

repo1 : master  
repo2 : dev  
repo3 : issue12

If you want to go further, the full ref contained in .git/HEAD is also a relative path to a file containing the SHA-1 hash of the branch's last commit.

faintsignal
  • 1,828
  • 3
  • 22
  • 30
joemaller
  • 19,579
  • 7
  • 67
  • 84
132

The * is expanded, what you can do is use sed instead of grep and get the name of the branch immediately:

branch=$(git branch | sed -n -e 's/^\* \(.*\)/\1/p')

And a version using git symbolic-ref, as suggested by Noufal Ibrahim

branch=$(git symbolic-ref HEAD | sed -e 's,.*/\(.*\),\1,')

To elaborate on the expansion, (as marco already did,) the expansion happens in the echo, when you do echo $test with $test containing * master then the * is expanded according to the normal expansion rules. To suppress this one would have to quote the variable, as shown by marco: echo "$test". Alternatively, if you get rid of the asterisk before you echo it, all will be fine, e.g. echo ${test:2} will just echo master. Alternatively you could assign it anew as you already proposed:

branch=${test:2}
echo $branch

This will echo master, like you wanted.

wich
  • 16,709
  • 6
  • 47
  • 72
  • 3
    It would be courteous of you to mention my answer if you used elements from it to update yours. – Noufal Ibrahim Jan 21 '10 at 17:24
  • Of course, my apologies, I updated quickly before leaving, missed the attribution. – wich Jan 22 '10 at 08:07
  • 2
    ok, so I tried both the methods, and I must say that `symbolic-ref` won't always work. eg: if you checkout some old commit, it throws error, whereas the `sed` command gives something like `(HEAD detached at 1qw23er)` – zhirzh Oct 30 '15 at 01:22
  • 3
    Note: the first technique works even if there are forward slashes in the branch name (e.g., because it is an issue URL), why the second one only takes the last segment in that case. – Aral Balkan Mar 30 '16 at 13:09
  • `sed` makes this even easier, actually! `branch=$(git branch | sed '/^ /d;s/^\* //')` – wilbur4321 May 24 '22 at 20:41
38

I would use the git-symbolic-ref command in the git core. If you say git-symbolic-ref HEAD, you will get the name of the current branch.

Noufal Ibrahim
  • 71,383
  • 13
  • 135
  • 169
  • 15
    In later version of git, you will have to use git symbolic-ref HEAD instead. – Jamey Hicks Jan 22 '10 at 13:03
  • 6
    You'd want to use the --short flag to only get the branch name. For an example: While on the master branch `git-symbolic-ref HEAD` outputs `refs/heads/master` but ` git-symbolic-ref --short HEAD` only outputs `master` – UTF_or_Death Apr 05 '19 at 12:11
23

I use this git describe --contains --all HEAD in my git helper scripts

example:

#!/bin/bash
branchname=$(git describe --contains --all HEAD)
git pull --rebase origin $branchname

I have that in a file called gpull in ~/scripts

Edit:

for a lot of CI environments, they'll check your code out in a "detached head" state, so then I'll use:

BRANCH=$(\
  git for-each-ref \
  --format='%(objectname) %(refname:short)' refs/heads \
  | awk "/^$(git rev-parse HEAD)/ {print \$2}"\
)
NullVoxPopuli
  • 61,906
  • 73
  • 206
  • 352
8

The problem relies on:

echo $test

In fact the variable test contains a wildcard which is expanded by the shell. To avoid that just protect $test with double quotes:

echo "$test"
marco
  • 4,455
  • 1
  • 23
  • 20
1

disable subshell glob expansion,

test=$(set -f; git branch)
Dyno Fu
  • 8,753
  • 4
  • 39
  • 64
  • 1
    that's not where the expansion happens, it's in the echo, as marco already elaborated upon. – wich Jan 22 '10 at 08:11