Git grep

May 05, 2019

Have you ever been bothered by a very long line when you grep the code and stumble upon a minified versioned file? You can of course ignore this line by specifiying to git grep. However, I wondered if we could prevent repeating this option every time. As I didn't find any satisfaying solution, I use this little trick.

Git Version I use: 2.21.0, if earlier, some changes have to be made to the following script.

First, I create a file .gitgrepignore with the same idea than .gitignore but only when grepping. It will permanently store all the directories and the files you want to ignore. If you don't want to version this file and share it with your team, just dereference it from the git index:

echo '.gitgrepignore' >> .git/info/exclude

Suppose you want to ignore the directory dist from all of you research, just add it to the .gitgrepignore:

echo 'dist' >> .gitgrepignore

One another example of .gitgrepignore:

package-lock.json

Now, we will override the default behavior of git grep. To do so, I use this stackoverflow post. It simply looks if you have an executable whose name is git-<your-command>. In our case, it's git-grep and replace the default by this command.

Just add this function to you .bashrc:

# Git supports aliases defined in .gitconfig, but you cannot override Git
# builtins (e.g. "git log") by putting an executable "git-log" somewhere in the
# PATH. Also, git aliases are case-insensitive, but case can be useful to create
# a negated command (gf = grep --files-with-matches; gF = grep
# --files-without-match). As a workaround, translate "X" to "-x".
git()
{
    typeset -r gitAlias="git-$1"
    if 'which' "$gitAlias" >/dev/null 2>&1; then
        shift
        "$gitAlias" "$@"
    elif [[ "$1" =~ [A-Z] ]]; then
        # Translate "X" to "-x" to enable aliases with uppercase letters.
        translatedAlias=$(echo "$1" | sed -e 's/[A-Z]/-\l\0/g')
        shift
        "$(which git)" "$translatedAlias" "$@"
    else
        "$(which git)" "$@"
    fi
}

Finally, let's just add the option :^<line-content> for every line in our file .gitgrepignore. I used bash, if you have another preferred shell, just adapt it. I put this file in my path, for example /usr/bin/git-grep.

#!/usr/bin/env bash

input=".gitgrepignore"

if [ ! -f $input ]; then
  # As the file .gitgrepignore does not exist, run the default behavior
  git grep "$@"
  exit 0
fi

opt=""
while IFS= read -r var
do
  # Check if the var is not an empty string
  if [ ! -z "$var" ]
  then
    opt="$opt :^$var"
  fi
done < "$input"

git grep "$@" $opt

Add execution right to be scanned by which:

sudo chmod +x /usr/bin/git-grep

Now you can add whatever file or directory you want in the .gitgrepignore, they will be ignored!

References:

  1. Git alias