Skip to content
masterclass Buddy

Git · Command

git grep

git grep searches text for patterns inside a Git repository. It can search the tracked files in your working tree, the staged content in the index, or any commit or tree, without checking that tree out.

Last updated

How it works

You give it one or more patterns and, optionally, trees and pathspecs. If you pass no tree, Git reads the tracked files in your working tree. --cached makes it read the index blobs instead, and naming a tree such as a tag or commit makes it read the blobs stored there. Several -e patterns are joined with OR by default, and you can build Boolean expressions with --and, --not and parentheses. Matches are printed as filename:line, and flags like -n, -c, -l or -p change the output. By default the patterns are POSIX basic regular expressions. Searches run on several threads, and the grep.* config keys can set your preferred defaults.

Syntax

git grep [<options>] [-e] <pattern> [--and|--or|--not|(|)|-e <pattern>...] [--cached | --untracked | --no-index | <tree>...] [--] [<pathspec>...]

Parameters

NameTypeDescription
<pattern>stringThe search expression. Put -e in front of it if it starts with a dash, and use -f <file> to read several patterns from a file.
<tree>...tree-ish (commit, tag, tree)Search the blobs in these trees instead of the tracked files in the working tree.
<pathspec>...pathspecOnly search paths that match at least one of these patterns. Leading paths, globs and magic such as :^ for exclusion all work.
--cached / --untracked / --no-indexflagChoose what to search: the index, the working tree plus untracked files, or a plain directory that Git doesn't manage.
-i, -w, -v, -E, -F, -PflagControl matching: ignore case, match whole words only, invert the match, or use extended, fixed-string or Perl regex syntax.
-n, -c, -l/-L, -o, -p, -W, -A/-B/-CflagControl output: line numbers, match counts, file names only, only the matched text, the enclosing function, or context lines.
--threads <num>integerHow many worker threads to use. This overrides grep.threads.

Returns

Prints the matches to stdout. With -q it prints nothing and returns exit status 0 if there was a match and a non-zero status if there wasn't.

Key points

  • --cached searches what is staged rather than what is on disk, so you can check the exact content your next commit will contain.
  • If you pass a commit, tag or tree name, Git searches that snapshot directly, so you can search old releases without switching branches.
  • --untracked adds untracked files to the search. Ignored files are still skipped unless you also pass --no-exclude-standard.
  • You can combine patterns with --and, --or, --not and escaped parentheses. OR is the default, and AND binds more tightly than OR.
  • Pathspecs narrow the search to certain paths. They accept globs such as '*.[ch]' and exclusions such as ':^Documentation'.
  • -W prints the whole function that contains each match, and -p prints only the function's header line. Both find functions the same way git diff finds hunk headers.
  • Patterns are POSIX basic regular expressions unless you pass -E, -F or -P, or set a different grep.patternType in your config.
  • -q makes git grep usable as a check in scripts: it prints nothing and reports through its exit status whether anything matched.

Common mistakes

  • -P (Perl regex) only works if your Git build includes PCRE support. Without it, git grep exits with an error instead of searching.
  • A pattern that starts with a dash is read as an option. Put -e in front of it, and always use -e in scripts that pass in user input.
  • If you search the index or a tree with --textconv, multiple threads can be slower than one. In that case try --threads=1.
  • --no-index cannot be combined with --cached or --untracked.

Example: Searching the working tree, the index and an old tag

# Search tracked C files for a word, with line numbers
git grep -n -w 'time_t' -- '*.[ch]'

# Count matches per file in the staged (index) content
git grep --cached -c 'TODO'

# Lines with #define AND (MAX_PATH or PATH_MAX) in tag v1.0, grouped by file
git grep --heading --break -n \
  -e '#define' --and \( -e MAX_PATH -e PATH_MAX \) v1.0

# Exclude a directory via pathspec magic
git grep 'solution' -- ':^Documentation'

# Use in a script: fail if a debug marker is staged
if git grep -q --cached 'console.log'; then
  echo 'Remove debug logging before committing' >&2
  exit 1
fi

These commands search the working tree, the index and an old tag, combine patterns with Boolean operators, exclude a directory with a pathspec, and use -q as a check in a script.

When to use it

Pre-commit guard

A hook runs git grep -q --cached to block commits whose staged files contain secrets or leftover debug calls.

Investigating an old release

While fixing a bug in a production tag, you search that tag's tree for a symbol without checking it out or touching your current work.

Refactoring impact check

Before renaming a function, git grep -l -w lists every tracked file that uses it, and -p shows which functions call it.

Searching a non-Git directory

git grep --no-index gives you Git's pathspec filtering and output formatting in a folder that is not a repository.

Interview questions

QHow is git grep different from running plain grep -r in a repository?

By default git grep only reads tracked files, so it skips build output, ignored files and the .git directory. It can also search the index (--cached) or any commit or tree directly, without a checkout. It supports pathspec filtering and runs on several threads.

Follow-up: How would you search ignored files too? Use git grep --untracked --no-index-free form: git grep --untracked --no-exclude-standard, because --no-exclude-standard only has an effect together with --untracked.

QHow do you find lines that contain '#define' together with either MAX_PATH or PATH_MAX?

Run git grep -e '#define' --and \( -e MAX_PATH -e PATH_MAX \). Several -e patterns are joined with OR by default, and --and binds more tightly than --or, so the escaped parentheses are needed to group the two alternatives.

Follow-up: What does --all-match do differently? It works at the file level: it only shows OR-matched lines from files that contain a match for every pattern, while --and requires all patterns on the same line.

QHow would you use git grep in a CI script to fail the build if a forbidden string exists?

Use git grep -q pattern and check the exit status. -q prints nothing and returns 0 when something matches and a non-zero status when nothing does. Put -e before the pattern if it could start with a dash.

Follow-up: How do you check only the staged content in a pre-commit hook? Add --cached, so the search reads the index blobs instead of the working-tree files, which may contain changes that aren't staged.

  • git log
  • git diff
  • git ls-files
  • git config
  • gitglossary
  • gitattributes

Source: Git - git-grep Documentation. Checked 2026-11-02.