Bagaimana mengecualikan direktori / file tertentu dari pencarian git grep

144

Apakah ada cara untuk mengecualikan path / direktori / file tertentu saat mencari repositori git menggunakan git grep? Sesuatu yang mirip dengan --excludeopsi pada grepperintah normal ?

Saya perlu menggunakan git grepkarena menggunakan greplangsung berjalan terlalu lambat pada repositori git besar.

Yogeshwer Sharma
sumber
Melakukannya dengan bash akan menjadi solusi yang mungkin: stackoverflow.com/questions/216995/…
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功 法轮功
8
Fitur ini ditambahkan pada 1.9.0 lihat jawaban saya di bawah ini
onlynone

Jawaban:

205

Dalam git 1.9.0 "kata ajaib" excludeditambahkan ke pathspecs. Jadi jika Anda ingin mencari foobardi setiap file kecuali yang cocok, *.javaAnda dapat melakukannya:

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

Atau menggunakan !"formulir singkat" untuk mengecualikan:

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

Perhatikan bahwa dalam versi git hingga v2.12, saat menggunakan pengecualian pathspec, Anda harus memiliki setidaknya satu "inklusif" pathspec. Dalam contoh di atas ini adalah ./*(secara rekursif menyertakan semuanya di bawah direktori saat ini). Dalam git v2.13 pembatasan ini dicabut dan git grep foobar -- ':!*.java'berfungsi tanpa ./*.

Anda juga dapat menggunakan sesuatu seperti :(top)(formulir pendek:) :/untuk memasukkan semuanya dari atas repo. Tetapi kemudian Anda mungkin juga ingin menyesuaikan pengecualian Anda pathspecuntuk memulai dari atas: :/!*.java(jika tidak, itu hanya akan mengecualikan *.javafile dari bawah direktori Anda saat ini).

Ada referensi yang baik untuk semua "kata ajaib" diperbolehkan dalam pathspecdi git-scm.com (atau hanya git help glossary). Untuk beberapa alasan, dokumen di kernel.org benar-benar ketinggalan zaman meskipun mereka sering muncul pertama kali dalam pencarian google.

hanya tidak ada
sumber
4
git grep clock.gettime -- './*' ':!arch/**' ':!drivers/**'untuk mengecualikan beberapa direktori secara keseluruhan. Saya tidak berpikir itu mencegah rekursi.
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
2
Untuk sering digunakan, Anda dapat membuat git alias dengan pengecualian: git config alias.mygrep '!git grep "$@" -- "${GIT_PREFIX}/*" ":!*.java*" #'. Lalu saja git mygrep foobar. (Menggunakan alias shell # trick dan dir . Saat ini)
medmunds
masalah yang tidak dapat saya pecahkan dengan solusi ini adalah bahwa path file yang dilaporkan relatif terhadap root WC. Jadi, jika saya berada di subdirektori dari WC, saya tidak bisa hanya menggunakan path file yang ditemukan (s) apa adanya (misalnya kurang) tetapi harus persimpangan jalur umum. Apakah ada solusi untuk ini (tanpa harus mengosongkan diri sendiri)? [git bash on win7]
elonderin
1
@elonder dalam solusi ini tidak ada hubungannya dengan bagaimana file yang cocok dilaporkan. Tapi saya baru saja mencoba git grepdan git ls-filesdari subdirektori dan keduanya melaporkan nama file relatif ke direktori saat ini (bahkan ketika Anda menggunakan ':(top)'pathspec termasuk). Kedua perintah memiliki --full-nameopsi untuk melaporkan nama relatif ke root, tapi itu tidak aktif secara default.
onlynone
1
Saya tidak menggunakan alias git jadi saya membuat fungsi bash, tapi mungkin alias git lebih baik gist.github.com/cmdcolin/04e2378b60f4457a41904c659368066f
Colin D
62

Pembaruan: Untuk git> = 1.9 ada dukungan asli untuk pola kecualikan, lihat hanya satu jawaban .

Ini mungkin tampak mundur, tetapi Anda dapat mengirimkan daftar file yang tidak cocok dengan pola kecualian Anda git grepseperti ini:

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

grep -vmengembalikan setiap jalur yang tidak cocok <exclude-pattern>. Catatan yang git ls-filesjuga mengambil --excludeparameter, tetapi itu hanya berlaku untuk file yang tidak dilacak .

kynan
sumber
Terima kasih untuk ini! Git grep jauh lebih cepat daripada ack & co tetapi tidak bisa mengecualikan jalur sewenang-wenang agak terlalu merepotkan untuk dikatakan :)
Tomasz Zieliński
2
Sayangnya repo saya memiliki banyak file. Ketika saya mencoba pendekatan @ kynan saya mendapatkan: "-bash: / usr / bin / git: Daftar argumen terlalu panjang"
Benissimo
2
Ini harus menyelesaikan masalah "Daftar argumen terlalu lama" dari Benissimo dan masalah saya dengan karakter nama file yang ditafsirkan oleh bash (seperti []) atau nama file yang mengandung spasi dalam repositori: git ls-files | grep -v <exclue-pattern> | xargs -d '\ n' git grep <pattern> -
Scout
2
Periksa onlynone's answer, itu mungkin untuk melakukan ini sepenuhnya dalam (versi modern) git sekarang.
David
Mengapa downvotes? Jawaban ini masih berlaku untuk versi git sebelum 1.9. Saya telah menambahkan catatan yang merujuk pada jawaban oneone.
kynan
5

Anda dapat menandai file atau direktori sebagai biner dengan membuat file atribut di repositori Anda, mis

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

Kecocokan dalam file biner terdaftar tanpa baris yang disertakan, misalnya

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

Dengan contoh oleh @kynan sebagai basis saya membuat skrip ini dan meletakkannya di path saya ( ~/bin/) sebagai gg. Itu memang menggunakan git greptetapi menghindari beberapa tipe file yang ditentukan.

Dalam repo kami ada banyak gambar jadi saya telah mengecualikan file gambar, dan ini mengambil waktu serchtime ke 1/3 jika saya mencari seluruh repo. Tetapi skrip dapat dengan mudah dimodifikasi untuk mengecualikan jenis file atau pola lain.

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

Catatan 1

Menurut ini , mungkin untuk memberi nama benda itu git-ggdan dapat menyebutnya sebagai perintah git biasa seperti:

$ git gg searchstring

Tapi saya tidak bisa membuatnya bekerja. Saya membuat skrip di saya ~/bin/dan membuat git-ggsymlink di /usr/lib/git-core/.

Catatan 2

Perintah tidak dapat dibuat menjadi shgit-alias biasa karena perintah itu akan dipanggil di root repo. Dan bukan itu yang saya inginkan!

UlfR
sumber