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!