1

I want to write a script that will run

git diff --name-status master..<BRANCH>

but when I run this:

for i in $(git branch | grep -v master); do
   echo $i;
done

I get echo one directory because git branch echo asterisk (I have one directory in current directory)

* <SELECTED BRANCH>

Why * is expanded and how can I prevent that expansion?

UPDATE: I can prevent this by using this:

for i in $(git branch | tr -d '*' | grep -v master);
done;

But why is this happening? Why I need to remove asterisk?

jcubic
  • 9,932

2 Answers2

4

Unquoted variables and command substitutions like $i or $(git …) apply the split+glob operator to the string result. That is:

  1. Build a string containing the value of the variable or the output of the command (minus final newlines in the latter case).
  2. Split the string into separate fields according to the value of IFS.
  3. Interpret each field as a wildcard pattern and replace it by the list of matching file names; if a pattern doesn't match any file, leave it intact.

The output of git branch | grep -v master (step 1) contains * master which is split (step 2) into two fields * and master; * is replaced (step 3) by the list of file names in the current directory.

You can run set -f to temporarily disable globbing. Another approach is to avoid command substitution and instead parse the input with read. Neither do the right thing though — you'll have spurious branch names, because the git output contains more than what you need (the * flag to indicate the current branch, but also things like remotes/somewhere/foo -> bar for remote-tracking branches). I think the following is safe if inelegant:

for i in $(git branch | sed -e 's/^..//' -e 's/ .*//'); do
   echo $i
done

I think the robust way is to use git-for-each-ref.

for i in $(git for-each-ref --format='%(refname:short)' refs/heads refs/remotes); do
  echo $i
done
0

Text streams like this should be read using a while loop rather than for, which is probably causing the issue (although I cannot reproduce it). A simple way to do this:

git branch | while IFS= read -r line
do
    echo "${line:2}"
done

Comparison:

$ cd -- "$(mktemp -d)"
$ git init
Initialized empty Git repository in /tmp/tmp.MJFmu7q7EH/.git/
$ touch README
$ git commit --allow-empty -m "Initial commit"
[master (root-commit) da46b03] Initial commit
$ git branch foo
$ git branch
  foo
* master
$ for i in $(git branch)
> do
>    echo $i
> done
foo
*
master
$ git branch | while IFS= read -r line
> do
>     echo "${line:2}"
> done
foo
master
l0b0
  • 51,350
  • Have the same with while, I'm using GNU bash, version 4.3.11(1)-release (i686-pc-linux-gnu). – jcubic Aug 20 '14 at 08:34