209

Is there a way to exclude certain paths/directories/files when searching a Git repository using git grep? Something similar to the --exclude option in the normal grep command?

I need to use git grep because using grep directly runs too slowly on large Git repositories.

2

6 Answers 6

314

In Git 1.9.0, the "magic word" exclude was added to pathspecs. So if you want to search for foobar in every file except for those matching *.java you can do:

git grep foobar -- ':(exclude)*.java' 

Or using the ! "short form" for exclude:

git grep foobar -- ':!*.java' 

Note that in Git versions up to v2.12, when using an exclude pathspec, you must have at least one "inclusive" pathspec. In the above examples, you'd want to add ./* (recursively include everything under the current directory) somewhere after the -- as well. In git v2.13 this restriction was lifted and git grep foobar -- ':!*.java' works without the ./*.

There's a good reference for all the "magic words" allowed in a pathspec at git-scm.com (or just git help glossary).

Sign up to request clarification or add additional context in comments.

9 Comments

git grep clock.gettime -- './*' ':!arch/**' ':!drivers/**' to exclude multiple entire directories. I don't think it prevents recursion though.
For frequent use, you can make a git alias with the exclusions: git config alias.mygrep '!git grep "$@" -- "${GIT_PREFIX}/*" ":!*.java*" #'. Then just git mygrep foobar. (Using alias shell # trick and current dir.)
@elonderin this solution has nothing to do with how the matched files are reported. But I just tried a git grep and git ls-files from subdirectories and both report filenames relative to the current directory (even when you use the ':(top)' include pathspec). Both commands have the --full-name option to report names relative to the root, but that's off by default.
I don't use git aliases so I made a bash function, but possibly a git alias is better gist.github.com/cmdcolin/04e2378b60f4457a41904c659368066f
I'm using git 2.19 and a simpler syntax than the one posted above that works is just: git grep foobar ':!*.java'
|
67

For Git 1.9 and later, there is native support for exclude patterns; see onlyone's answer.

For older versions of Git: This may seem backwards, but you can pass a list of files not matching your exclude pattern to git grep like this:

git grep <pattern> -- `git ls-files | grep -v <exclude-pattern>` 

grep -v returns every path not matching <exclude-pattern>. Note that git ls-files also takes a --exclude parameter, but that is only applied to untracked files.

5 Comments

Thanks for this! Git grep is so much faster than ack & co but not being able to exclude arbitrary paths was a bit too inconvenient so to speak :)
Unfortunately my repo has a lot of files. When I try @kynan's approach I get: "-bash: /usr/bin/git: Argument list too long"
This should solve both the "Argument list too long" problem of Benissimo and my problem with filename caracters interpreted by bash (like []) or filenames containing spaces in the repository: git ls-files | grep -v <exclue-pattern> | xargs -d '\n' git grep <pattern> --
Check onlynone's answer, it's possibly to do this entirely within (modern versions of) git now.
Why the downvotes? This answer still applies to git versions prior to 1.9. I've added a note referring to onlyone's answer.
27

You can mark files or directories as binary by creating an attributes file in your repository, e.g.,

File .git/info/attributes

directory/to/ignore/*.* binary directory/to/ignore/*/*.* binary another_directory/to/also/ignore/*.* binary 

Matches in binary files are listed without the including line, e.g.,

git grep "bar" 

Output:

Binary file directory/to/ignore/filename matches other_directory/other_filename: foo << bar - bazz[:whatnot] 

2 Comments

I found that dir-i-wanted-to-ignore/**/* binary did the trick.
Best answer. Can also be committed to the repo by using .gitattributes instead of local .git/info/attributes.
19

It is not possible in older versions of Git, but it was discussed. A proposed workaround is in the link:

You can put *.dll to .gitignore file then git grep --exclude-standard.

See also onlynone's answer, since Git 1.9.0 it's possible.

1 Comment

This used to be true but no longer, it is now possible in git. See what should be the real answer below: stackoverflow.com/a/30084612/1391445
2

With the example by kynan as the base, I made this script and put it in my path (~/bin/) as gg. It does use git grep, but it avoids some specified filetypes.

In our repository, it’s a lot of images, so I have excluded the imagefiles, and this takes the search time down to 1/3 if I search the whole repository. But the script could easily be modified to exclude other filetypes or geleralpatterns.

#!/bin/bash # # Wrapper of git-grep that excludes certain filetypes. # NOTE: The filetypes to exclude is hard coded for my specific needs. # # The basic setup of this script is from here: # https://stackoverflow.com/a/14226610/42580 # But there is issues with giving extra path information to the script # therefor I crafted the while-thing that moves path-parts to the other side # of the '--'. # Declare the filetypes to ignore here EXCLUDES="png xcf jpg jpeg pdf ps" # Rebuild the list of file endings to a good regexp EXCLUDES=`echo $EXCLUDES | sed -e 's/ /\\\|/g' -e 's/.*/\\\.\\\(\0\\\)/'` # Store the stuff that is moved from the arguments. moved= # If git-grep returns this "fatal..." then move the last element of the # arg-list to the list of files to search. err="fatal: bad flag '--' used after filename" while [ "$err" = "fatal: bad flag '--' used after filename" ]; do { err=$(git grep "$@" -- `git ls-files $moved | grep -iv "$EXCLUDES"` \ 2>&1 1>&3-) } 3>&1 # The rest of the code in this loop is here to move the last argument in # the arglist to a separate list $moved. I had issues with whitespace in # the search-string, so this is loosely based on: # http://www.linuxjournal.com/content/bash-preserving-whitespace-using-set-and-eval x=1 items= for i in "$@"; do if [ $x -lt $# ]; then items="$items \"$i\"" else moved="$i $moved" fi x=$(($x+1)) done eval set -- $items done # Show the error if there was any echo $err 

Note 1

According to this, it should be possible to name the thing git-gg and be able to call it as a regular Git command like:

git gg searchstring 

But I can not get this working. I created the script in my ~/bin/ folder and made the git-gg symbolic link in folder /usr/lib/git-core/.

Note 2

The command can not be made into an regular sh git-alias since it will then be invoked at the root of the repository. And that is not what I want!

Comments

1

We can also use the ^ operator to exclude the files:

git grep foobar -- ':^*.java' # Excludes the files ending with .java from search 

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.