Como excluir determinados diretórios / arquivos da pesquisa git grep

144

Existe uma maneira de excluir certos caminhos / diretórios / arquivos ao pesquisar um repositório git usando git grep? Algo semelhante à --excludeopção no grepcomando normal ?

Eu preciso usar git grepporque o uso grepé executado diretamente muito lentamente em grandes repositórios git.

Yogeshwer Sharma
fonte
Fazê-lo no bash seria uma solução possível: stackoverflow.com/questions/216995/…
Ciro Santilli escreveu:
8
Esse recurso foi adicionado na versão 1.9.0. Veja minha resposta abaixo
somente

Respostas:

205

No git 1.9.0, a "palavra mágica" excludefoi adicionada a pathspecs. Portanto, se você deseja pesquisar foobarem todos os arquivos, exceto os correspondentes, *.javapode:

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

Ou usando o !"formato abreviado" para excluir:

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

Observe que nas versões git até a v2.12, ao usar uma exclusão pathspec, você deve ter pelo menos uma "inclusiva" pathspec. Nos exemplos acima, este é o ./*(inclua recursivamente tudo no diretório atual). No git v2.13, essa restrição foi levantada e git grep foobar -- ':!*.java'funciona sem o ./*.

Você também pode usar algo como :(top)(formato abreviado:) :/para incluir tudo, desde a parte superior do repositório. Mas então você provavelmente também desejaria ajustar sua exclusão pathspecpara começar do topo: :/!*.java(caso contrário, apenas excluiria *.javaarquivos do diretório atual).

Há uma boa referência para todas as "palavras mágicas" permitidas pathspecno site git-scm.com (ou apenas git help glossary). Por alguma razão, os documentos em kernel.org estão realmente desatualizados, embora geralmente sejam os primeiros nas pesquisas do Google.

onlynone
fonte
4
git grep clock.gettime -- './*' ':!arch/**' ':!drivers/**'excluir vários diretórios inteiros. Eu não acho que isso evite a recursão.
Ciro Santilli escreveu:
2
Para uso frequente, você pode fazer um alias de git com as exclusões: git config alias.mygrep '!git grep "$@" -- "${GIT_PREFIX}/*" ":!*.java*" #'. Então apenas git mygrep foobar. (Usando nome falso desembolsar truque # e dir atual .)
medmunds
o problema que não consigo resolver com esta solução é que os caminhos relatados dos arquivos são relativos à raiz do WC. Portanto, se eu estiver em um sub-diretório do WC, não posso usar o caminho do (s) arquivo (s) encontrado (s) como está (por exemplo, por menos), mas tenho que juntar caminhos comuns. Existe uma solução para isso (sem ter que me seduzir)? [git bash no win7]
elonderin 26/09
1
@elonderin esta solução não tem nada a ver com a forma como os arquivos correspondentes são relatados. Mas eu só tentei um git grepe git ls-filesde subdiretórios e ambos os nomes de arquivos de relatórios relativos ao diretório atual (mesmo quando você usa a ':(top)'incluir pathspec). Ambos os comandos têm a --full-nameopção de relatar nomes relativos à raiz, mas estão desativados por padrão.
onlynone
1
Eu não uso aliases git, então criei
Colin D
62

Atualização: Para git> = 1.9, há suporte nativo para padrões de exclusão, consulte apenas a resposta de alguém .

Isso pode parecer invertido, mas você pode passar uma lista de arquivos que não correspondem ao seu padrão de exclusão para git grep:

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

grep -vretorna todos os caminhos que não correspondem <exclude-pattern>. Observe que git ls-filestambém usa um --excludeparâmetro, mas que é aplicado apenas a arquivos não rastreados .

kynan
fonte
Obrigado por isso! Git grep é muito mais rápido do que ack & co, mas não ser capaz de excluir caminhos arbitrários foi um pouco inconveniente, por assim dizer :)
Tomasz Zieliński
2
Infelizmente meu repositório tem muitos arquivos. Quando tento a abordagem do @ kynan, recebo: "-bash: / usr / bin / git: lista de argumentos muito longa"
Benissimo
2
Isso deve resolver o problema da Benissimo "Argument list too long" e o meu problema com caracteres de nome de arquivo interpretados por bash (como []) ou nomes de arquivos que contêm espaços no repositório: git ls-files | grep -v <exclue-pattern> | xargs -d '\ n' git grep <padrão> -
Scout
2
Verifique apenas a resposta de ninguém; é possível fazer isso inteiramente dentro das (versões modernas do) git agora.
David
Por que os votos negativos? Esta resposta ainda se aplica às versões git anteriores à 1.9. Adicionei uma nota referente à resposta de onlyone.
Kynan
5

Você pode marcar arquivos ou diretórios como binários, criando um arquivo de atributos em seu repositório, por exemplo

$ cat .git/info/attributes 
directory/to/ignore/*.* binary
directory/to/ignore/*/*.* binary
another_directory/to/also/ignore/*.* binary

As correspondências nos arquivos binários são listadas sem a linha de inclusão, por exemplo

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

Com o exemplo de @kynan como base, criei esse script e o coloquei no meu caminho ( ~/bin/) como gg. Ele usa, git grepmas evita alguns tipos de arquivos especificados.

Em nosso repositório, há muitas imagens, então excluí os arquivos de imagem, e isso leva o tempo de espera para 1/3, se eu pesquisar no repositório inteiro. Mas o script pode ser facilmente modificado para excluir outros tipos de arquivos ou padrões de geleral.

#!/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                                                                      

Nota 1

De acordo com isso , deve ser possível nomear a coisa git-gge poder chamá-la como um comando git normal, como:

$ git gg searchstring

Mas não consigo fazer isso funcionar. Eu criei o script no meu ~/bin/e fiz o git-gglink simbólico /usr/lib/git-core/.

Nota 2

O comando não pode ser transformado em um shgit-alias regular , pois será invocado na raiz do repositório. E não é isso que eu quero!

UlfR
fonte