Cómo excluir ciertos directorios / archivos de la búsqueda de git grep

144

¿Hay alguna manera de excluir ciertas rutas / directorios / archivos al buscar en un repositorio git usando git grep? ¿Algo similar a la --excludeopción en el grepcomando normal ?

Necesito usarlo git grepporque usarlo grepdirectamente se ejecuta muy lentamente en grandes repositorios git.

Yogeshwer Sharma
fuente
Hacerlo en bash sería una posible solución: stackoverflow.com/questions/216995/…
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
8
Esta característica se agregó en 1.9.0, vea mi respuesta a continuación
solo

Respuestas:

206

En git 1.9.0 la "palabra mágica" excludese agregó a pathspecs. Entonces, si desea buscar foobaren todos los archivos, excepto los que coinciden *.java, puede hacer:

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

O usando la !"forma corta" para excluir:

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

Tenga en cuenta que en las versiones de git hasta v2.12, cuando se utiliza una exclusión pathspec, debe tener al menos un "inclusivo" pathspec. En los ejemplos anteriores, este es el ./*(incluya recursivamente todo en el directorio actual). En git v2.13 esta restricción se levantó y git grep foobar -- ':!*.java'funciona sin el ./*.

También puede usar algo como :(top)(forma corta:) :/para incluir todo desde la parte superior del repositorio. Pero es probable que también desee ajustar su exclusión pathspecpara comenzar también desde arriba: :/!*.java(de lo contrario, solo excluiría *.javaarchivos de su directorio actual).

Hay una buena referencia para todas las "palabras mágicas" que puede contener un pathspeca git-scm.com (o sólo git help glossary). Por alguna razón, los documentos en kernel.org están realmente desactualizados, aunque a menudo aparecen primero en las búsquedas de Google.

solo nadie
fuente
44
git grep clock.gettime -- './*' ':!arch/**' ':!drivers/**'para excluir múltiples directorios completos. Sin embargo, no creo que evite la recursión.
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
2
Para un uso frecuente, se puede hacer un alias de git con las exclusiones: git config alias.mygrep '!git grep "$@" -- "${GIT_PREFIX}/*" ":!*.java*" #'. Entonces solo git mygrep foobar. (Usando alias shell # trick y dir .
Actual
El problema que no puedo resolver con esta solución es que las rutas informadas de los archivos son relativas a la raíz del WC. Entonces, si estoy en un subdirectorio del WC, no puedo usar la ruta de los archivos encontrados tal cual (por ejemplo, por menos) pero tengo que juntar rutas comunes. ¿Hay alguna solución para esto (sin tener que vaciarme)? [git bash en win7]
elonderin
1
@elonderin esta solución no tiene nada que ver con cómo se informan los archivos coincidentes. Pero he intentado una git grepy git ls-filesde dos subdirectorios y los nombres de archivo de informes relativos al directorio actual (incluso cuando se utiliza la ':(top)'incluyo pathspec). Ambos comandos tienen la --full-nameopción de informar nombres relativos a la raíz, pero eso está desactivado de manera predeterminada.
onlynone
1
No uso alias git, así que hice una función bash, pero posiblemente un alias git sea mejor gist.github.com/cmdcolin/04e2378b60f4457a41904c659368066f
Colin D
62

Actualización: para git> = 1.9 hay compatibilidad nativa para excluir patrones, consulte la respuesta de oneone .

Esto puede parecer al revés, pero puede pasar una lista de archivos que no coinciden con su patrón de exclusión para que le git grepguste esto:

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

grep -vdevuelve cada ruta que no coincide <exclude-pattern>. Tenga en cuenta que git ls-filestambién toma un --excludeparámetro, pero que solo se aplica a los archivos no rastreados .

kynan
fuente
¡Gracias por esto! Git grep es mucho más rápido que ack & co, pero no ser capaz de excluir rutas arbitrarias fue demasiado inconveniente, por así decirlo :)
Tomasz Zieliński
2
Lamentablemente mi repositorio tiene muchos archivos. Cuando intento el enfoque de @ kynan me sale: "-bash: / usr / bin / git: Lista de argumentos demasiado larga"
Benissimo
2
Esto debería resolver tanto el problema "Lista de argumentos demasiado larga" de Benissimo como mi problema con los caracteres de nombre de archivo interpretados por bash (como []) o nombres de archivo que contienen espacios en el repositorio: git ls-files | grep -v <exclue-pattern> | xargs -d '\ n' git grep <patrón> -
Scout
2
Comprueba solo la respuesta de nadie, es posible hacer esto completamente dentro de (versiones modernas de) git ahora.
David
¿Por qué los votos negativos? Esta respuesta todavía se aplica a las versiones de git anteriores a 1.9. He agregado una nota que se refiere a la respuesta de oneone.
kynan
5

Puede marcar archivos o directorios como binarios creando un archivo de atributos en su repositorio, p. Ej.

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

Las coincidencias en archivos binarios se enumeran sin la línea incluida, p. Ej.

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

Con el ejemplo de @kynan como base, hice este script y lo puse en mi camino ( ~/bin/) como gg. Sí utiliza, git greppero evita algunos tipos de archivo especificados.

En nuestro repositorio hay muchas imágenes, así que he excluido los archivos de imagen, y esto reduce el tiempo de búsqueda a 1/3 si busco en todo el repositorio. Pero el script podría modificarse fácilmente para excluir otros tipos de archivos o patrones gelerales.

#!/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 acuerdo con esto , debería ser posible nombrar la cosa git-ggy poder llamarla como un comando git regular como:

$ git gg searchstring

Pero no puedo hacer que esto funcione. Creé el script en mi ~/bin/e hice un git-ggenlace simbólico en /usr/lib/git-core/.

Nota 2

El comando no se puede convertir en un shgit-alias regular , ya que se invocará en la raíz del repositorio. ¡Y eso no es lo que quiero!

UlfR
fuente