Learn git-grep to boost your command line search!

September 5, 2019

When searching for contents inside files from command-line, we probably think of find and grep, but how many of us have heard about git grep?

When searching inside your work tree, I found it more convenient than find/ grep. Here is how you can get started with git-grep today!

Things to know

Git grep searches inside project with git. The only requirement is the project need to be tracked (hint: git add .). It will work without having to commit it.

Let's git greppin'

The pattern is very simple. Do git grep {pattern} anywhere inside your project directory.

  • To search for "foo":
git grep foo

Here are other useful variants:

  • Search with line number
git grep -n foo
  • Returning only file name:
git grep -l foo
  • It is regex-compatible.
git grep "f[^\s]*\s"
  • To tell how many match in each file:
git grep -c foo
  • To only look under certain file extension (ignore matches from other files)
git grep foo *.js

and / or

  • To search for "foo" or "bar", we can do:
git grep -e foo -e bar
  • To search for a file containing "foo" and "bar" and they are on the same line:
git grep -e foo --and -e bar

To search for a file containing "foo" and "bar", not necessarily on the same line:

git grep --all-match -e foo -e bar

There are more (check man git-grep, but above are the ones I find useful).

Getting fancy: search within and between commits

The real power of git grep is its ability to search inside ANY commit we want.

Here are the two that I found very useful:

  • Search inside specific commit

There are two ways to do it: reference it relative to HEAD and reference its SHA.

git grep "foo" HEAD~1 // search ONLY in one commit before current head
git grep "foo" a1b2 // search ONLY in commit a1b2
  • Search inside multiple commits

It also accepts multiple commit references.

git grep "foo" HEAD HEAD~1 HEAD~7 // search in current head, previous head, and 7th from head.
git grep "foo" a1b2 HEAD~5 // search inside a1b2 SHA and 5th from head
  • Search everything between 2 commits

This uses git rev-list command. Here are some quick refreshers:

git rev-list HEAD~3..HEAD // Returns all commits between HEAD~3 and HEAD.
git rev-list --all // Returns ALL Commits from the dawn of time to present day.

Combining rev-list with git-grep, we can do:

  • Search for all occurrences of "foo" between HEAD~3 and HEAD
git grep foo $(git rev-list HEAD~3..HEAD)
  • Search for ALL occurrences of "foo" from the very beginning to current.
git grep foo $(git rev-list --all)

Of course, we can put together everything we learned. This returns filename(s) from every single commit we have that contain, in each file, "bar" and anything that matches f[^\s]*\s pattern.

git grep --all-match -l -e "f[^\s]*\s" -e bar $(git rev-list --all)

That's it! I find git grep more convenient than traditional grep or find when I need to search file contents inside working tree. Of course, find/grep have their own usage that git grep can't do, but that's for another time.

Resources

Happy hacking!