4

Most advanced uses of git for-each-ref that I've come across involve eval. For instance, the last example in the git-for-each-ref man page uses eval in order to execute the contents of the fmt variable:

#!/bin/sh

fmt='
    r=%(refname)
    # ... omitted, for conciseness ...
    '

eval=`git for-each-ref --shell --format="$fmt" \
    # ... omitted, for conciseness ...
    refs/tags`
eval "$eval"

However, the use of eval is associated with security risks; avoiding it, whenever possible, is considered good practice.

Here is a real example, adapted from this answer:

#!/bin/sh

fmt='
    ref=%(refname:short)

    if git merge-base --is-ancestor $1 $ref; then
      printf "%s\n" "$ref"
    fi
'

eval "$(git for-each-ref --shell --format="$fmt" refs/heads/)"

In this particular example, how can I avoid using eval? I've reviewed the options listed in Zenexer's answer, but I couldn't find one that does the trick. I'm looking for as portable (across different shells) a solution as possible.

1
  • IMHO it is hackish to do so, even if the man page suggests that. You'll find better ways than eval. You might for example create a json,xml,csv (or whatever) file with git for-each-ref and use it as input for a shell script or shell function. Commented Aug 11, 2015 at 9:21

1 Answer 1

3
+200

Instead of treating data as code using eval, you let git for-each-ref output a stream of data in a format that is easy for you to process. Then, you write a custom processor for that data.

git for-each-ref --format "<values>" \
     # more options
     refs/tags | while read refname object_type <more args> ; do
          <code>
     done

As for the specific example you gave, here is an equivalent non-eval version:

#!/bin/bash

if [ $# -ne 1 ]; then
    printf "usage: git branchesthatcontain <rev>\n\n"
    exit 1
fi

rev=$1

git for-each-ref --format='%(refname:short)' refs/heads \
    | while read ref; do 
          if git merge-base --is-ancestor "$rev" "$ref"; then
              echo "$ref"
          fi;
      done

exit $?

I must add that git-for-each-ref does include --shell, --python and --tcl flags which ensures that the data is properly escaped: this is not the same scenario as in the accepted answer to the question you reference.

This question and the associated answer are also relevant.

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

5 Comments

For completeness, I would also like to see the method applied to an actual example. I'll edit my question and add one there.
@Jubobs Here it is. I removed the part about nul-delimited strings because I don't see a way to actually prevent the command from outputting newlines, even though %00 would output the NUL character. But this is not needed either since for-each-ref can quote the output.
Great stuff. I like the stream approach, which seems much more functional in spirit. Can I update my answer, if I properly acknowledge yours?
@Jubobs Thanks; and yes, of course, you can update your answer (I did not notice it was yours ;-) )
Thanks. 200 pts of rep' well spent :)

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.