Jak wykluczyć określone katalogi / pliki z wyszukiwania git grep

144

Czy istnieje sposób na wykluczenie pewnych ścieżek / katalogów / plików podczas przeszukiwania repozytorium git przy użyciu git grep? Coś podobnego do --excludeopcji w normalnym greppoleceniu?

Muszę użyć, git grepponieważ używanie grepbezpośrednio działa zbyt wolno w dużych repozytoriach git.

Yogeshwer Sharma
źródło
Robienie tego na bashu byłoby możliwym obejściem: stackoverflow.com/questions/216995/…
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
8
Ta funkcja została dodana w 1.9.0, zobacz moją odpowiedź poniżej
brak

Odpowiedzi:

206

W git 1.9.0 "magiczne słowo" excludezostało dodane do pathspecs. Więc jeśli chcesz szukać foobarw każdym pliku oprócz tych pasujących *.java, możesz zrobić:

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

Lub używając !„krótkiego formularza” do wykluczania:

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

Zwróć uwagę, że w wersjach git do wersji 2.12, gdy używasz wykluczenia pathspec, musisz mieć co najmniej jedną opcję „włączającą” pathspec. W powyższych przykładach jest to ./*(rekursywnie dołączaj wszystko w bieżącym katalogu). W git v2.13 to ograniczenie zostało zniesione i git grep foobar -- ':!*.java'działa bez rozszerzenia ./*.

Możesz również użyć czegoś takiego jak :(top)(krótka forma :/:), aby uwzględnić wszystko od góry repozytorium. Ale wtedy prawdopodobnie chciałbyś również dostosować wykluczenie, pathspecaby również rozpoczynał się od góry: :/!*.java(w przeciwnym razie wykluczałoby to tylko *.javapliki z bieżącego katalogu).

Jest dobrym punktem odniesienia dla wszystkich „magicznych słów” w sposób dozwolony pathspecna git-scm.com (lub tylko git help glossary). Z jakiegoś powodu dokumenty na kernel.org są naprawdę nieaktualne, mimo że często pojawiają się jako pierwsze w wyszukiwaniach Google.

tylko brak
źródło
4
git grep clock.gettime -- './*' ':!arch/**' ':!drivers/**'aby wykluczyć wiele całych katalogów. Nie sądzę jednak, aby zapobiegało to rekursji.
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
2
Do częstego stosowania, można dokonać aliasu git z wyłączeniami: git config alias.mygrep '!git grep "$@" -- "${GIT_PREFIX}/*" ":!*.java*" #'. Wtedy po prostu git mygrep foobar. (Używając aliasu shell # trick i bieżącego reż .)
medmunds
Problem, którego nie mogę rozwiązać za pomocą tego rozwiązania, polega na tym, że zgłaszane ścieżki plików są względne w stosunku do katalogu głównego WC. Tak więc, jeśli jestem w podkatalogu WC, nie mogę po prostu użyć ścieżki znalezionego pliku (plików) bez zmian (np. Za mniej), ale muszę połączyć wspólne ścieżki. Czy istnieje rozwiązanie tego problemu (bez konieczności samodzielnego umieszczania go)? [git bash on win7]
elonderin
1
@elonderin to rozwiązanie nie ma nic wspólnego z raportowaniem dopasowanych plików. Ale właśnie wypróbowałem podkatalogi git grepi git ls-filesz podkatalogów i oba raportują nazwy plików względem bieżącego katalogu (nawet jeśli używasz ':(top)'ścieżki dołączania). Oba polecenia mają --full-nameopcję zgłaszania nazw względem katalogu głównego, ale jest to domyślnie wyłączone.
onlynone
1
Nie używam aliasów git, więc utworzyłem funkcję bash, ale prawdopodobnie alias git jest lepszy gist.github.com/cmdcolin/04e2378b60f4457a41904c659368066f
Colin D
62

Aktualizacja: dla git> = 1.9 istnieje natywna obsługa wzorców wykluczania, zobacz odpowiedź onlyone .

Może się to wydawać odwrotne, ale możesz przekazać listę plików, które nie pasują do wzorca wykluczania, w git grepten sposób:

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

grep -vzwraca każda ścieżka nie pasujące <exclude-pattern>. Zauważ, że git ls-filesrównież pobiera --excludeparametr, ale jest on stosowany tylko do nieśledzonych plików .

kynan
źródło
Dzięki za to! Git grep jest o wiele szybszy niż ACK & CO, ale brak możliwości wykluczenia dowolnych ścieżek był trochę zbyt niewygodny, że tak powiem :)
Tomasz Zieliński
2
Niestety moje repozytorium ma dużo plików. Kiedy próbuję podejścia @ kynan, otrzymuję: „-bash: / usr / bin / git: lista argumentów jest za długa”
Benissimo
2
Powinno to rozwiązać zarówno problem „Zbyt długa lista argumentów” Benissimo, jak i mój problem z znakami nazw plików interpretowanymi przez bash (np. []) Lub nazwami plików zawierających spacje w repozytorium: git ls-files | grep -v <wzorzec-wykluczenia> | xargs -d '\ n' git grep <wzór> -
Scout
2
Sprawdź odpowiedź onlynone, prawdopodobnie zrobisz to teraz w całości w (nowoczesnych wersjach) git.
David
Dlaczego głosy przeciw? Ta odpowiedź nadal dotyczy wersji git wcześniejszych niż 1.9. Dodałem notatkę odnoszącą się do odpowiedzi tylko jednej osoby.
kynan
5

Możesz oznaczyć pliki lub katalogi jako binarne, tworząc plik atrybutów w swoim repozytorium, np

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

Dopasowania w plikach binarnych są wymienione bez linii włączającej, np

$ git grep "bar"
Binary file directory/to/ignore/filename matches
other_directory/other_filename:      foo << bar - bazz[:whatnot]
coberlin
źródło
2

Z przykładem @kynan jako bazą utworzyłem ten skrypt i umieściłem go w mojej path ( ~/bin/) jako gg. Używa, git grepale unika niektórych określonych typów plików.

W naszym repozytorium jest dużo obrazów, więc wykluczyłem pliki obrazów, a to skraca czas wyszukiwania do 1/3, jeśli przeszukam całe repozytorium. Ale skrypt można łatwo zmodyfikować, aby wykluczyć inne typy plików lub wzorce 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                                                                      

Notatka 1

W związku z tym powinno być możliwe nazwanie rzeczy git-ggi wywołanie jej jako zwykłej komendy git, takiej jak:

$ git gg searchstring

Ale nie mogę uruchomić tego. Utworzyłem skrypt w moim ~/bin/i utworzyłem git-ggłącze symboliczne w /usr/lib/git-core/.

Uwaga 2

Polecenie nie może zostać przekształcone w zwykły shalias git, ponieważ zostanie wywołane w katalogu głównym repozytorium. A nie tego chcę!

UlfR
źródło