Skip to content

Instantly share code, notes, and snippets.

@darcyparker
Last active June 6, 2018 17:47
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save darcyparker/d5b9168ada88d0527564a11d9d9899c4 to your computer and use it in GitHub Desktop.
Save darcyparker/d5b9168ada88d0527564a11d9d9899c4 to your computer and use it in GitHub Desktop.
git grep for a git repo and its submodules
#!/usr/bin/env bash
# Author: darcyparker@gmail.com
# Repo: https://gist.github.com/darcyparker/d5b9168ada88d0527564a11d9d9899c4
# Description:
# * git grep for the repo and its submodules.
# * greps into submodules recrusively
# * Output includes full relative path from the top level repo to files with matches
# * Output is formatted with line number, and column number of matched
# Usage:
# gitgrep "some string"
# gitgrep "some string" some/dir
# gitgrep "some string" some/dir --extra-option1 --extra-option2
# * where `--extra-option1` and `--extra-option2` are `git grep` options
# Tip:
# Add a git alias in your `~/.gitconfig` like this:
# [alias]
# grepall = "!f() { gitgrep "$1" "$2" "$3"; }; f"
f(){
pushd . > /dev/null
local _searchString="$1"; shift
if [[ $# -ge 1 ]] && [ -d "$1" ]; then
cd "$1" || exit; shift
fi
if [ -d .git ] || git rev-parse --git-dir > /dev/null 2>&1; then
local _rootDir
_rootDir="$(git rev-parse --show-toplevel)"
else
popd > /dev/null
return
fi
local _args=( "git" "grep" "-n" "--no-color" )
#Add optional extra args
if [[ $# -ge 1 ]] && [ ! -z "$1" ]; then
_args=( "${_args[@]}" "$1[@]" )
fi
_args=( "${_args[@]}" "$_searchString" -- )
cd "$_rootDir" || exit
#Two sets of results: 1) from the git repo and 2) each git submodule
{
"${_args[@]}" "$_rootDir"
git --no-pager submodule --quiet foreach --recursive "echo \"\$toplevel/\$path\"" | \
while read -r _subModuleDir; do
cd "$_subModuleDir" || exit
"${_args[@]}" "$_subModuleDir" | while read -r _line; do
#Output filepath relative to $_rootDir
echo "${_subModuleDir#$_rootDir/}/$_line"
done
done
} | \
#Now pipe it to a formatter that includes path, line and column numbers and matched line
while read -r _line; do
local _filePathAndLineNum
_filePathAndLineNum=$(echo "$_line" | cut -d: -f1,2)
local _matched
_matched=${_line/$_filePathAndLineNum:/''}
local _columnNum
_columnNum=$(echo "$_matched" | awk "{print index(\$0, \"$_searchString\")}")
echo "$_filePathAndLineNum:$_columnNum:$_matched"
done
popd > /dev/null
}
f "$@"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment