Git geçmişinde kaydedilmiş kod nasıl grep (arama)


1434

Geçmişte bir dosya veya bazı kodları bir dosyada sildim. İçeriği gizleyebilir miyim (işleme iletilerinde değil)?

Çok kötü bir çözüm günlüğü grep etmektir:

git log -p | grep <pattern>

Ancak, bu işlem sağlama karmasını hemen geri döndürmez. git grepBoşuna oynamadım .


2
Junio ​​C tarafından yazılmış bu blog gönderileri Hamano (git keeper) sizin için ilginç olabilir: * Linus'un nihai içerik izleme aracı (kazma arama yani git log -Sve suçlama hakkında) * ["git log --grep" ile eğlence] [2] (arama iletileri) ) * ["Git grep" ile eğlence] [3] [2]: gitster.livejournal.com/30195.html [3]: gitster.livejournal.com/27674.html
Jakub Narębski


olası yinelenen yanıt gerçekten işe yarıyor: stackoverflow.com/a/1340245/492
CAD bloke

Bu konuda bir değişiklik vermediğidir .. yani kim / ne zaman
Sonic Soul

Yanıtlar:


1889

Kesinti içeriğini (yani, iletiler ve benzerlerinin aksine gerçek kaynak satırları) aramak için şunları yapmanız gerekir:

git grep <regexp> $(git rev-list --all)

git rev-list --all | xargs git grep <expression> "Bağımsız değişken listesi çok uzun" hatasıyla karşılaşırsanız çalışır.

Aramayı bazı alt ağaçlarla (örneğin, "lib / util") sınırlamak istiyorsanız, bunu rev-listalt komutlara iletmeniz ve grepayrıca:

git grep <regexp> $(git rev-list --all -- lib/util) -- lib/util

Bu, tüm taahhüt metninizi gözden geçirecektir regexp.

Her iki komutta da yolun geçmesinin nedeni, rev-listtüm değişikliklerin lib/utilgerçekleştiği revizyonlar listesini döndürecek , ancak aynı zamanda grepyalnızca arama yapacak şekilde geçmeniz gerektiğidir lib/util.

Sadece aşağıdaki senaryoyu düşünün: grepaynı <regexp>döndürülen revizyonda bulunan diğer dosyalarda da aynı şeyi bulabilir rev-list(bu revizyonda o dosyada değişiklik olmasa bile).

Kaynağınızı aramanın diğer bazı yararlı yolları:

Çalışma ağacında normal ifade normal ifadesiyle eşleşen metni arayın:

git grep <regexp>

Çalışma ağacında normal ifade regexp1 veya regexp2 ile eşleşen metin satırlarını arayın:

git grep -e <regexp1> [--or] -e <regexp2>

Çalışma ağacında yalnızca normal dosya yollarını bildiren normal ifade regexp1 ve regexp2 ile eşleşen metin satırlarını arayın:

git grep -l -e <regexp1> --and -e <regexp2>

Çalışma ağacında normal ifade normal ifadesiyle eşleşen metin satırları ve normal ifade normal ifadesiyle eşleşen metin satırları olan dosyaları arayın:

git grep -l --all-match -e <regexp1> -e <regexp2>

Çalışma ağacında, eşleşen metin eşleme desen satırlarını arayın:

git diff --unified=0 | grep <pattern>

Tüm düzeltmeleri normal ifade normal ifadesiyle eşleşen metni arayın:

git grep <regexp> $(git rev-list --all)

Normal ifade normal ifadesiyle eşleşen metni bulmak için rev1 ve rev2 arasındaki tüm düzeltmeleri arayın:

git grep <regexp> $(git rev-list <rev1>..<rev2>)

61
Teşekkürler, harika çalışıyor! Yine de "$ (git rev-list --all)" gereklidir ve bir dalın tüm geçmişinde aramayı belirtmek için uygun bir anahtar yoktur.
Ortwin Gentz

3
Mükemmel. +1. GitBook bazı ayrıntılar ekler ( book.git-scm.com/4_finding_with_git_grep.html ) ve Junio ​​C Hamano bazı noktalarınızı gösterir: gitster.livejournal.com/27674.html
VonC

18
Ne yazık ki, msysgit-1.7.4 ile devam edemiyorum. Bana söylüyor sh.exe": /bin/git: Bad file number. VonC'nin yanıtı msysgit ile de çalışır.
eckes

4
Rev-list ile git grep geçmişini çağırdığınızda "ağaç okunamıyor" hatası alıyorsanız, bazı şeyleri temizlemeniz gerekebilir. Deneyin git gcveya bir göz
Anthony Panozzo

8
Evet, bu Windows'ta da başarısız görünüyor, ne yazık ki.
mlissner

552

İçin kazma ( -S) seçeneğini kullanmalısınız git log.

Aramak için Foo:

git log -SFoo -- path_containing_change
git log -SFoo --since=2009.1.1 --until=2010.1.1 -- path_containing_change

Bkz Git tarihini - Anahtar kelime ile kayıp satırı bulun daha fazlası.


As Jakub Narębski yorumladı:

  • bu , bir örneğini tanıtan veya bu örneği kaldıran farklılıkları arar<string> . Bu genellikle "'Foo' ile satır eklediğiniz veya kaldırdığınız düzeltmeler" anlamına gelir.

  • --pickaxe-regexseçenek genişletilmiş POSIX yerine bir dize için arama regex kullanmasına olanak sağlar. Örnek (itibaren git log):git log -S"frotz\(nitfol" --pickaxe-regex


As Rob yorumladı, bu arama harf duyarlıdır - o açtı takip soruyu harf duyarsız arama için nasıl.


3
Teşekkürler, bu seçeneğin farkında değildim. Taahhüt mesajlarıyla ilgileniyorsanız bunun en iyi çözüm olduğu ve saf satır eşleştirmenin geleneksel UNIX grep davranışına ihtiyacınız varsa Jeet'in çözümü en uygun çözüm gibi görünüyor.
Ortwin Gentz

@Ortwin: kabul etti (ve seçilen çözümü iptal ettim). git logsorunuzu biraz beni karıştırmıştım;)
VonC

12
Farkı -pçıkartmak için bayrakla birleştirin .
Sander

Git log -S kullanarak belirli bir kalıpla eşleşen tüm dizinleri hariç tutmanın bir yolu var mı?
BakaKuna

3
@Anentropik --branches --alltüm repo aramak için seçenek gerekir .
VonC

249

Bunu yapmanın en sevdiğim yolu git log' -Gseçeneğidir (1.7.4 sürümünde eklendi).

-G<regex>
       Look for differences whose added or removed line matches the given <regex>.

Bir işlemin eşleşip eşleşmediğini -Gve -Sseçeneklerinin belirleme şekli arasında küçük bir fark vardır :

  • -SSeçenek aslında önce ve bir taahhüt sonra bir dosyaya kez arama eşleşmeleri sayısını sayar. Önceki ve sonraki sayımlar farklıysa, taahhüt günlüğünde gösterilir. Bu, örneğin, aramanızla eşleşen bir satırın nereye taşındığını gösterir.
  • Bu -Gseçenekle, aramanız eklenmiş, kaldırılmış veya değiştirilmiş herhangi bir satırla eşleşirse, taahhüt günlükte gösterilir.

Bu taahhüdü örnek olarak alalım:

diff --git a/test b/test
index dddc242..60a8ba6 100644
--- a/test
+++ b/test
@@ -1 +1 @@
-hello hello
+hello goodbye hello

Dosyada "merhaba" ifadesinin görüntülenme sayısı, bu işlemden önce ve sonra aynı olduğundan, ile eşleşmez -Shello. Bununla birlikte, bir çizgi eşleşmesinde bir değişiklik olduğundan hello, taahhüt kullanılarak gösterilir -Ghello.


2
Git günlük çıktısında eşleşen değişiklik içeriğini göstermenin bir yolu var mı?
Thilo-Alexander Ginkel

13
@ Thilo-AlexanderGinkel - Genellikle -pher taahhüt için fark gösterme seçeneğini eklerim . Sonra günlük çağrı cihazımda açıldığında, aradığım her şeyi ararım. Çağrı cihazı ise lessve siz git log -Ghello -pyazabilirsiniz /hellobasın Enterve kullanımı nve N"Merhaba" sonraki / önceki olaylar bulmak için.
Tyler Holien

İlginç bir sorun buldum -Gve Regex: Komut satırı UTF-8 kullanıyor ve baktığınız dosya bazı ISO-Latin (8 bit) kodlaması kullanıyorsa .*başarısız olur. Örneğin, bir değişiklik var Vierter Entwurf-> Fünfter Entwurf, ve 'V.*ter Entwurf'bir eşleşme üretirken 'F.*ter Entwurf', değil.
U. Windl

51

Kod değişikliklerine göz atmak istiyorsanız (tüm geçmişte verilen kelime ile neyin değiştiğine bakın) patchmoda geçin - Yapmanın çok yararlı bir kombinasyonunu buldum:

git log -p
# Hit '/' for search mode.
# Type in the word you are searching.
# If the first search is not relevant, hit 'n' for next (like in Vim ;) )

11
Kabul edilen çözüm benim için git git -S işe yaramaz. Bunu yaptı!
rodvlopes

29

git log özellikle çok sayıda eşleşme varsa ve önce daha yeni (alakalı) değişiklikler görmek istiyorsanız, tüm şubelerde metin aramanın daha etkili bir yolu olabilir.

git log -p --all -S 'search string'
git log -p --all -G 'match regular expression'

Bu günlük komutları listesi, belirli bir arama dizesini / regex'i (genellikle) daha önce ekleyen veya kaldıran taahhüt eder. -pOpsiyon model eklendi veya kaldırıldı nereye bağlam içinde görebilirsiniz alakalı fark, gösterildiği neden olur.

Aradığınız metni ekleyen alakalı bir taahhüt bulduktan sonra (örneğin, 8beeff00d), taahhüdü içeren şubeleri bulun:

git branch -a --contains 8beeff00d

Merhaba, bu çizgiler işe yaramıyor gibi görünüyor. Komutum> git log -p --all -S 'genel dizesi DOB {get; Ayarlamak; } = string.Empty; ' ve her çalıştırmayı denediğimde ölümcül: belirsiz argüman 'string' alıyorum: bilinmeyen revizyon veya çalışma ağacında olmayan yol. > Yolları revizyonlardan ayırmak için '-' kullanın, şöyle:> 'git <command> [<revision> ...] - [<file> ...]'
user216652

@ user216652 Herhangi bir nedenle 'tırnak işaretleri arama dizenizi tek bir bağımsız değişken olarak gruplandırmıyor. Bunun yerine, 'publicargüman -Sve geri kalanını ayrı argümanlar olarak ele alıyor. Hangi ortamda çalıştığınızdan emin değilim, ancak sorun gidermeye yardımcı olması için bu bağlam gerekli olacaktır. Git komutunuzun kabuğa nasıl gönderildiği ile ilgili tüm bağlamlarda, sorun gidermenize yardımcı olması için ayrı bir StackOverflow sorusu açmanızı öneririm. Bana öyle geliyor ki başka bir komutla gönderiliyor mu? Buradaki yorumlar bunu anlamak için doğru yer değil.
Edward Anderson

26

Jeet'in cevabını aldım ve Windows'a uyarladım ( bu cevap sayesinde ):

FOR /F %x IN ('"git rev-list --all"') DO @git grep <regex> %x > out.txt

Benim için, bir nedenden ötürü, bu normal ifadeyi silen gerçek taahhüdün komutun çıktısında görünmediğini, onun yerine bir taahhüt olduğunu görüyorum.


2
+1 - ve her bulduktan sonra "q" tuşuna basmak istemiyorsanız --no-pager, sonunda git komutuna ekleyin
cgp

2
Ayrıca, bir metin dosyasına eklemenin aslında eşleşen metni görüntülemenin ek bir avantajına sahip olduğunu unutmayın. ( >>results.txtWindows borularında usta olmayanlar için bir metin dosyasına ekleyin ...
cgp

1
Ve
bash'ın

23

Ara herhangi revizyonu, herhangi bir dosya :

git rev-list --all | xargs git grep <regexp>

Yalnızca belirli dosyalarda, örneğin XML dosyalarında arama yapın:

git rev-list --all | xargs -I{} git grep <regexp> {} -- "*.xml"

Sonuç satırları şöyle görünmelidir: 6988bec26b1503d45eb0b2e8a4364afb87dde7af: bla.xml: bulduğu satırın metni ...

Daha sonra kullanarak yazar, tarih ve fark gibi daha fazla bilgi edinebilirsiniz git show:

git show 6988bec26b1503d45eb0b2e8a4364afb87dde7af

11

Basit olması için GUI: gitk - Git depo tarayıcısını kullanmanızı öneririm . Oldukça esnek

  1. Kodu aramak için:

    Resim açıklamasını buraya girin
  2. Dosya aramak için:

    Resim açıklamasını buraya girin
  3. Elbette, düzenli ifadeleri de destekler:

    Resim açıklamasını buraya girin

Ve yukarı / aşağı okları kullanarak sonuçlarda gezinebilirsiniz.


6

Sourcetree'de bunu yapmaya çalışan herkes için, UI'de bunun için doğrudan bir komut yoktur (1.6.21.0 sürümünden itibaren). Ancak, Terminal penceresini açarak (ana araç çubuğunda bulunan düğme) ve bunları kopyalayıp yapıştırarak kabul edilen cevapta belirtilen komutları kullanabilirsiniz .

Not: Sourcetree'nin Arama görünümünde kısmen sizin için metin araması yapılabilir. Arama görünümüne gitmek için Ctrl+ tuşuna basın 3(veya altta bulunan Ara sekmesini tıklayın). En sağdan, Arama türünü Dosya Değişiklikleri olarak ayarlayın ve ardından aramak istediğiniz dizeyi yazın. Bu yöntem yukarıdaki komutla karşılaştırıldığında aşağıdaki sınırlamalara sahiptir:

  1. Sourcetree yalnızca değiştirilen dosyalardan birinde arama kelimesini içeren taahhütleri gösterir . Arama metnini içeren dosyanın tam olarak bulunması yine manuel bir iştir.
  2. RegEx desteklenmez.

4

Kendimi bulunduğunuz yerde bulduğumda, aşağıdaki komut satırını kullanıyorum:

git log -S "<words/phrases i am trying to find>" --all --oneline  --graph

Açıklama:

  1. git log- Burada daha fazla yazmam gerekiyor; günlükleri kronolojik sırada gösterir.
  2. -S "<words/phrases i am trying to find>" - Git'in herhangi bir dosyanın (eklenen / değiştirilen / silinen) '<>' sembolü olmadan bulmaya çalıştığım kelimeleri / ifadeleri içerdiği tüm bu taahhütleri gösterir.
  3. --all - Tüm şubeleri zorlamak ve aramak.
  4. --oneline - Git günlüğünü bir satırda sıkıştırır.
  5. --graph - Kronolojik sıralı işlemlerin grafiğini oluşturur.

1
"Kendimi senin yerinde bulduğumda git'i kullanma ihtiyacını hissediyorum!"
Sebi

1
Bu harika bir cevap!
Alf Eaton

@AlfEaton benim zevkim!
surajs1n

2

Jeet'in cevabı PowerShell'de çalışır.

git grep -n <regex> $(git rev-list --all)

Aşağıdakiler a içeren tüm dosyaları görüntüler password.

# Store intermediate result
$result = git grep -n "password" $(git rev-list --all)

# Display unique file names
$result | select -unique { $_ -replace "(^.*?:)|(:.*)", "" }

1

Peki kodun eski sürümlerini gözden geçirmeye çalışıyorsunuz.

Eğer bunu yapsaydım, muhtemelen git bisect kullanırdım . Bisect'i kullanarak, sürümün iyi mi kötü mü olduğunu kontrol eden bilinen iyi bir sürümü, bilinen kötü sürümü ve basit bir komut dosyasını belirtebilirsiniz (bu durumda aradığınız kodun mevcut olup olmadığını görmek için bir grep ). Bunu çalıştırdığınızda, kod kaldırıldığında bulunacaktır.


2
Evet, ancak "test" kod için çırpınan ve kod varsa "true", yoksa "false" döndüren bir komut dosyası olabilir.
Rob Di Marco

2
Peki, revizyon 10'da kod kötüyse, revizyon 11'de iyi olur ve revizyon 15'te tekrar kötüleşir ...
Paolo

2
Paolo'ya katılıyorum. İkili arama yalnızca "sıralı" değerler için uygundur. Git bisect durumunda bu, tüm "iyi" revizyonların referans noktasından başlayarak tüm "kötü" revizyonlardan önce geldiği anlamına gelir, ancak geçici kod ararken bu varsayım yapılamaz. Bu çözüm bazı durumlarda işe yarayabilir, ancak iyi bir genel amaçlı çözüm değildir.
Kent

Ben bütün ağaç bisect için birden fazla kez kontrol olarak bu son derece verimsiz olduğunu düşünüyorum.
U. Windl

0

Senaryo: IDE'nizi kullanarak kodunuzu çok temizlediniz. Sorun: IDE gerektiğinden daha fazla temizlendi ve şimdi kod derlenmiyor (eksik kaynaklar, vb.)

Çözüm:

git grep --cached "text_to_find"

"Text_to_find" dosyasının değiştirildiği dosyayı bulur.

Artık bu değişikliği geri alabilir ve kodunuzu derleyebilirsiniz.


0
git rev-list --all | xargs -n 5 git grep EXPRESSION

Bir çimdik olan Jeet çözümüyle , bu arar ve sadece (büyük bir depoda uzun zaman alabilir) sonunda ise bu nedenle sonuçları gösterir.


-1

Benim durumumda kısa bir taahhüt aramam gerekiyordu ve listelenen çözümler maalesef işe yaramıyordu.

Ben ( REGEX belirteci yerine ) ile başardı :

for commit in $(git rev-list --all --abbrev-commit)
do
    if [[ $commit =~ __REGEX__ ]]; then 
        git --no-pager show -s --format='%h %an - %s' $commit
    fi
done
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.