Comment exclure certains répertoires / fichiers de la recherche git grep


144

Existe-t-il un moyen d'exclure certains chemins / répertoires / fichiers lors de la recherche d'un référentiel git à l'aide de git grep? Quelque chose de similaire à l' --excludeoption de la grepcommande normale ?

J'ai besoin d'utiliser git grepparce que l'utilisation grepdirecte s'exécute trop lentement sur les grands dépôts git.


Le faire sur bash serait une solution de contournement possible: stackoverflow.com/questions/216995/…
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

8
Cette fonctionnalité a été ajoutée dans la version 1.9.0 voir ma réponse ci
seulement

Réponses:



206

Dans git 1.9.0, le "mot magique" a excludeété ajouté à pathspecs. Donc, si vous voulez rechercher foobardans tous les fichiers à l'exception de ceux qui correspondent, *.javavous pouvez faire:

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

Ou en utilisant la !"forme courte" pour exclure:

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

Notez que dans les versions git jusqu'à la v2.12, lorsque vous utilisez une exclusion pathspec, vous devez avoir au moins un "inclusif" pathspec. Dans les exemples ci-dessus, il s'agit du ./*(inclure récursivement tout sous le répertoire courant). Dans git v2.13, cette restriction a été levée et git grep foobar -- ':!*.java'fonctionne sans l' extension ./*.

Vous pouvez également utiliser quelque chose comme :(top)(forme courte:) :/pour tout inclure depuis le haut du dépôt. Mais alors vous voudrez probablement également ajuster votre exclusion pathspecpour qu'elle commence également par le haut: :/!*.java(sinon, cela n'exclurait que les *.javafichiers de votre répertoire actuel).

Il existe une bonne référence pour tous les «mots magiques» autorisés dans un pathspecat git-scm.com (ou juste git help glossary). Pour une raison quelconque, les documents sur kernel.org sont vraiment obsolètes même s'ils apparaissent souvent en premier dans les recherches Google.


4
git grep clock.gettime -- './*' ':!arch/**' ':!drivers/**'pour exclure plusieurs répertoires entiers. Je ne pense pas que cela empêche la récursivité.
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

2
Pour une utilisation fréquente, vous pouvez faire un alias git avec les exclusions: git config alias.mygrep '!git grep "$@" -- "${GIT_PREFIX}/*" ":!*.java*" #'. Alors juste git mygrep foobar. (Utilisation de l'alias shell # trick et current dir .)
medmunds

le problème que je ne peux pas résoudre avec cette solution est que les chemins signalés des fichiers sont relatifs à la racine WC. Donc, si je suis dans un sous-répertoire du WC, je ne peux pas simplement utiliser le chemin du (des) fichier (s) trouvé (s) tel quel (par exemple pour moins) mais je dois junc chemins communs. Y a-t-il une solution à cela (sans devoir m'employer)? [git bash sur win7]
elonderin le

1
@elonderin cette solution n'a rien à voir avec la façon dont les fichiers correspondants sont signalés. Mais je viens d'essayer un git grepet git ls-filesdes sous-répertoires et les deux rapportent les noms de fichiers par rapport au répertoire actuel (même lorsque vous utilisez le ':(top)'chemin include). Les deux commandes ont la --full-namepossibilité de rapporter les noms relatifs à la racine, mais c'est désactivé par défaut.
seulement le

1
Je n'utilise pas d'alias git, j'ai donc créé une fonction bash, mais peut-être qu'un alias git est préférable gist.github.com/cmdcolin/04e2378b60f4457a41904c659368066f
Colin D

62

Mise à jour: pour git> = 1.9, il existe un support natif pour les modèles d'exclusion, voir la réponse d'une seule .

Cela peut sembler à l'envers, mais vous pouvez passer une liste de fichiers ne correspondant pas à votre modèle d'exclusion pour git grepaimer ceci:

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

grep -vrenvoie tous les chemins qui ne correspondent pas<exclude-pattern> . Notez que git ls-filesprend également un --excludeparamètre, mais qui n'est appliqué qu'aux fichiers non suivis .


Merci pour cela! Git grep est tellement plus rapide que ack & co mais ne pas pouvoir exclure des chemins arbitraires était un peu trop gênant pour ainsi dire :)
Tomasz Zieliński

2
Malheureusement, mon dépôt contient beaucoup de fichiers. Quand j'essaye l'approche de @ kynan, j'obtiens: "-bash: / usr / bin / git: Liste d'arguments trop longue"
Benissimo

2
Cela devrait résoudre à la fois le problème "Liste d'arguments trop longue" de Benissimo et mon problème avec les caractères de nom de fichier interprétés par bash (comme []) ou les noms de fichiers contenant des espaces dans le référentiel: git ls-files | grep -v <exclue-pattern> | xargs -d '\ n' git grep <modèle> -
Scout

2
Ne vérifiez que la réponse de personne, il est peut-être possible de le faire entièrement dans (les versions modernes de) git maintenant.
David

Pourquoi les votes négatifs? Cette réponse s'applique toujours aux versions de git antérieures à 1.9. J'ai ajouté une note faisant référence à la réponse d'une seule personne.
kynan

5

Vous pouvez marquer des fichiers ou des répertoires comme binaires en créant un fichier d'attributs dans votre référentiel, par exemple

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

Les correspondances dans les fichiers binaires sont listées sans la ligne d'inclusion, par exemple

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

2

Avec l'exemple de @kynan comme base, j'ai créé ce script et l'ai mis dans mon chemin ( ~/bin/) sous la forme gg. Il utilise git grepmais évite certains types de fichiers spécifiés.

Dans notre repo, il y a beaucoup d'images, j'ai donc exclu les fichiers image, ce qui réduit le temps de recherche à 1/3 si je recherche tout le repo. Mais le script pourrait facilement être modifié pour exclure d'autres types de fichiers ou modèles de gel.

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

Note 1

Selon cela, il devrait être possible de nommer l'objet git-gget de pouvoir l'appeler comme une commande git régulière comme:

$ git gg searchstring

Mais je ne peux pas faire fonctionner cela. J'ai créé le script dans my ~/bin/et créé le git-gglien symbolique dans /usr/lib/git-core/.

Note 2

La commande ne peut pas être transformée en un shgit-alias normal car elle sera alors appelée à la racine du dépôt. Et ce n'est pas ce que je veux!

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.