如何从git grep搜索中排除某些目录/文件

How to exclude certain directories/files from git grep search

有没有一种方法可以使用git grep搜索Git存储库,但从搜索中排除某些路径/目录/文件?就像正常grep命令中的--exclude选项一样。

如果您好奇的话:我不想使用普通grep,因为当git存储库的大小很大时,它比git grep慢得多。


在git 1.9.0中,在pathspecs中添加了"magic word"exclude。因此,如果要在每个文件中搜索foobar,除了匹配*.java的文件外,您可以执行以下操作:

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

或使用!的"短格式"排除:

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

注意,当使用exclude pathspec时,必须至少有一个"inclusive"pathspec。在上面的例子中,这是./*(递归地包括当前目录下的所有内容)。

您还可以使用类似于:(top)的东西(简称::/)来包含回购协议顶部的所有内容。但是,您可能还需要调整exclude pathspec从顶部开始::/!*.java(否则,它将只从当前目录下排除*.java文件)。

在git-scm.com(或仅在git help glossary)上,pathspec中允许的所有"魔力词"都有很好的参考。出于某种原因,kernel.org上的文档确实已经过时了,尽管它们在谷歌搜索中经常排在第一位。


更新:对于git>=1.9,存在对排除模式的本机支持,请参见OnlyOne的答案。

这可能看起来是向后的,但您可以将与排除模式不匹配的文件列表传递给git grep,如下所示:

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

grep -v返回与不匹配的每个路径。注意,git ls-files也接受一个--exclude参数,但这只适用于未跟踪的文件。


这是不可能的,但最近已经讨论过了。链接中的建议解决方案:

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

编辑-只查看你的答案,因为Git 1.9.0是可能的。


以@kynan的例子为基础,我编写了这个脚本,并将它作为gg放在我的路径中(~/bin/)。它确实使用了git grep,但避免了某些指定的文件类型。

在我们的repo中,它有很多图像,所以我排除了图像文件,如果我搜索整个repo,这将把serchtime降到1/3。但是可以很容易地修改脚本以排除其他文件类型或geleralPatterns。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
#!/bin/bash                                                                    
#                                                                              
# Wrapper of git-grep that excludes certain filetypes.                        
# NOTE: The filetypes to exclude is hardcoded 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 fileendings 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

注释1

根据这一点,应该可以将该对象命名为git-gg,并可以将其称为常规的git命令,例如:

1
$ git gg searchstring

但我做不到。我在我的~/bin/中创建了脚本,并在/usr/lib/git-core/中创建了git-gg符号链接。

注释2

该命令不能成为常规的shgit别名,因为它随后将在repo的根目录中调用。这不是我想要的!


通过在存储库中创建属性文件,可以将文件或目录标记为二进制文件,例如:

1
2
3
4
$ cat .git/info/attributes
directory/to/ignore/*.* binary
directory/to/ignore/*/*.* binary
another_directory/to/also/ignore/*.* binary

二进制文件中的匹配项没有包含行,例如

1
2
3
$ git grep"bar"
Binary file directory/to/ignore/filename matches
other_directory/other_filename:      foo << bar - bazz[:whatnot]