Git履歴でコミットされたコードをgrep(検索)する方法


1434

過去にファイルまたはファイル内のコードを削除したことがあります。(コミットメッセージではなく)コンテンツをgrepできますか?

非常に貧弱な解決策は、ログをgrepすることです。

git log -p | grep <pattern>

ただし、これはコミットハッシュをすぐには返しません。git grep無駄に遊んだ。


2
Junio C Hamano(gitメンテナー)によるこれらのブログ投稿はあなたにとって興味深いかもしれません:* Linusの究極のコンテンツ追跡ツール(つるはし検索ie git log -Sとblameについて)* ["git log --grep"で楽しむ] [2](コミットメッセージの検索)* [「git grep」で楽しい] [3] [2]:gitster.livejournal.com/30195.html [3]:gitster.livejournal.com/27674.html
JakubNarębski10年


可能性のある重複からの答えは実際に機能します:stackoverflow.com/a/1340245/492
CADが

これに関する問題は、それが変更にコンテキストを与えないということです。すなわち、誰/いつ
Sonic Soul

回答:


1889

コミットコンテンツ(つまり、コミットメッセージなどではなく実際のソース行)を検索するには、次の操作を行う必要があります。

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

git rev-list --all | xargs git grep <expression> 「引数リストが長すぎます」エラーが発生した場合に機能します。

検索を特定のサブツリー(たとえば、「lib / util」)に制限する場合は、それをrev-listサブコマンドに渡す必要がありgrepます。

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

これにより、のすべてのコミットテキストがgrepさ​​れますregexp

両方のコマンドでパスを渡す理由は、rev-listがすべての変更が行われlib/utilたリビジョンリストを返すためですがgrep、でのみ検索するように渡す必要もありますlib/util

次のシナリオを想像してみてください。によって返された同じリビジョンに含まれている他のファイルでgrep同じものが見つかる場合があります(そのリビジョンでそのファイルに変更がなかった場合でも)。<regexp>rev-list

ソースを検索するその他の便利な方法は次のとおりです。

作業ツリーで正規表現regexpに一致するテキストを検索します。

git grep <regexp>

作業ツリーで、正規表現regexp1またはregexp2に一致するテキストの行を検索します。

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

作業ツリーを検索して、正規表現regexp1およびregexp2に一致するテキストの行を検索し、ファイルパスのみを報告します。

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

正規表現regexp1に一致するテキストの行と正規表現regexp2に一致するテキストの行があるファイルを作業ツリーで検索します。

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

パターンに一致するテキストの変更された行を作業ツリーで検索します。

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

正規表現regexpに一致するテキストのすべてのリビジョンを検索します。

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

正規表現regexpに一致するテキストについて、rev1とrev2の間のすべてのリビジョンを検索します。

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

61
ありがとうございます。「$(git rev-list --all)」が必要であり、ブランチの履歴全体を検索するよう指定する便利なスイッチがないのは残念です。
Ortwin Gentz、2010年

3
優秀な。+1。GitBookはいくつかの詳細(book.git-scm.com/4_finding_with_git_grep.html)を追加し、Junio C Hamanoはあなたのポイントのいくつかを示しています:gitster.livejournal.com/27674.html
VonC

18
残念ながら、msysgit-1.7.4ではうまくいきません。教えてくれsh.exe": /bin/git: Bad file numberます。VonCの回答はmsysgitでも機能します。
eckes

4
rev-listを使用してgit grep履歴を呼び出すときに「ツリーを読み取ることができません」エラーが発生した場合は、クリーンアップが必要になることがあります。試してみてくださいgit gcまたはチェックアウト:stackoverflow.com/questions/1507463/...
アンソニーPanozzo

8
ああ、これはWindowsでも失敗するようです。
mlissner 2012年

552

pickaxe(-Sオプションを使用する必要がありますgit log

検索するにはFoo

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

詳細については、Gitの履歴を参照してください。


ヤクブNarębskiのコメント:

  • これは、のインスタンスを導入または削除する違いを探します<string>。通常、「 'Foo'で行を追加または削除したリビジョン」を意味します。

  • この--pickaxe-regexオプションを使用すると、文字列を検索する代わりに、拡張POSIX正規表現を使用できます。例(からgit log):git log -S"frotz\(nitfol" --pickaxe-regex


ロブはコメントし、この検索では大文字と小文字が区別さ-彼は開かれたフォローアップの質問大文字と小文字を区別しない検索する方法についてを。


3
おかげで、私はこのオプションを知らなかった。コミットメッセージに関心がある場合はこれが最良のソリューションであり、純粋な行マッチングの従来のUNIX grep動作が必要な場合はJeetのソリューションが最適です。
Ortwin Gentz、2010年

@Ortwin:同意しました(選択したソリューションに賛成しています)。git logあなたの質問のビットは私を混乱させました;)
VonC

12
-pフラグと組み合わせて、diffも出力します。
サンダー2014年

git log -Sを使用して特定のパターンに一致するすべてのディレクトリを除外する方法はありますか?
BakaKuna 14

3
@Anentropic --branches --allでは、すべてのリポジトリを検索するためのオプションが必要になります。
VonC、2015

249

私のお気に入りの方法は、git log-Gオプション(バージョン1.7.4で追加)を使用することです。

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

-G-Sオプションがコミットが一致するかどうかを決定する方法には微妙な違いがあります:

  • この-Sオプションは基本的に、コミットの前後にファイル内で検索が一致した回数をカウントします。前後のカウントが異なる場合、コミットはログに表示されます。たとえば、検索に一致する行が移動されたコミットは表示されません。
  • この-Gオプションを使用すると、追加、削除、または変更された行が検索と一致した場合、コミットがログに表示されます。

このコミットを例にとります:

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

ファイルに "hello"が現れる回数は、このコミットの前後で同じであるため、を使用した場合とは一致しません-Shello。ただし、行の一致に変更があったためhello、コミットはを使用して表示され-Ghelloます。


2
gitログ出力で一致する変更コンテキストを表示する方法はありますか?
Thilo-Alexander Ginkel 2014

13
@ Thilo-AlexanderGinkel-通常-p、各コミットの差分を表示するオプションを追加します。次に、ページャーでログを開くと、探しているものをすべて検索します。ポケットベルがlessandである場合git log -Ghello -p、と入力して/helloを押しEnter、とを使用nNて「hello」の次/前の出現を検索できます。
Tyler Holien 2014

-GとRegexで興味深い問題が見つかりました。コマンドラインでUTF-8を使用していて、表示しているファイルがISOラテン(8ビット)エンコーディングを使用している.*場合、失敗します。例えば、私は変更を持っているVierter Entwurf> - Fünfter Entwurf、しばらくは'V.*ter Entwurf'、マッチを生成'F.*ter Entwurf'しません。
U.ウィンドル

51

コードの変更を閲覧したい場合(履歴全体で特定の単語によって実際に変更されたものを確認する場合)、patchモードに移動します。次の操作の非常に便利な組み合わせが見つかりました。

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
認められた解決策は私にとってもgit log -Sでも機能しません。これはやった!
rodvlopes

29

git log 特に多くの一致があり、より最近の(関連する)変更を最初に表示したい場合は、すべてのブランチにわたってテキストを検索するためのより効果的な方法になります。

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

これらのログコマンドは、特定の検索文字列/正規表現を(一般的に)より新しいものから最初に追加または削除するコミットをリストします。この-pオプションを使用すると、パターンが追加または削除された場所に関連するdiffが表示されるため、コンテキストで確認できます。

探していたテキストを追加する関連コミット(たとえば、8beeff00d)を見つけたら、そのコミットを含むブランチを見つけます。

git branch -a --contains 8beeff00d

こんにちは、これらの行はまったく機能していないようです。私のコマンドは> git log -p --all -S 'public string DOB {get; セットする; } = string.Empty; ' 実行しようとするたびに、次のエラーが発生します> fatal:あいまいな引数 'string':作業ツリーにない不明なリビジョンまたはパス。>次のように、 '-'を使用してパスをリビジョンから分離します。> 'git <command> [<revision> ...]-[<file> ...]'
user216652

@ user216652何らかの理由で、'引用符が検索文字列を単一の引数としてグループ化していない。代わりにが'publicの引数-Sであり、残りは別の引数として扱われます。どの環境で実行しているかはわかりませんが、トラブルシューティングを行うにはそのコンテキストが必要です。トラブルシューティングに役立つ場合は、別のStackOverflow質問を開くことをお勧めします。gitコマンドがシェルに送信される方法のすべてのコンテキストが含まれます。他のコマンドで送信されているようですが?ここのコメントは、これを理解するのに適切な場所ではありません。
エドワードアンダーソン

26

私はJeetの答えを受け取り、それをWindowsに適合させました(この答えのおかげで):

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

私にとって、何らかの理由で、この正規表現を削除した実際のコミットは、コマンドの出力ではなく、その前の1つのコミットに表示されていたことに注意してください。


2
+ --no-pager
1-

2
また、テキストファイルに追加すると、一致するテキストを実際に表示できるという利点が追加されます。(>>results.txtWindowsパイピングに精通していないものを使用してテキストファイルに追加します...
cgp '28

1
そして、私はbashの構文は醜いと思いました:)
smido '22

23

リビジョン、ファイルを検索:

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

XMLファイルなど、特定のファイルのみを検索します。

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

結果の行は次のようになります。6988bec26b1503d45eb0b2e8a4364afb87dde7af:bla.xml:見つかった行のテキスト...

次に、作成者、日付、diffなどの詳細情報を取得できますgit show

git show 6988bec26b1503d45eb0b2e8a4364afb87dde7af

11

簡単にするために、GUIを使用することをお勧めしますgitk-Gitリポジトリブラウザー。それはかなり柔軟です

  1. コードを検索するには:

    ここに画像の説明を入力してください
  2. ファイルを検索するには:

    ここに画像の説明を入力してください
  3. もちろん、正規表現もサポートしています。

    ここに画像の説明を入力してください

また、上/下矢印を使用して結果をナビゲートできます。


6

Sourcetreeでこれを実行しようとしている他の人にとって、UIに直接コマンドはありません(バージョン1.6.21.0以降)。ただし、ターミナルウィンドウ(メインツールバーにあるボタン)を開いてコピー/貼り付けすることにより、承認された回答で指定されたコマンドを使用できます。

注:Sourcetreeの検索ビューでは、部分的にテキスト検索を実行できます。Ctrl+ 3を押して検索ビューに移動します(または下部にある[検索]タブをクリックします)。右端から、[検索の種類]を[ファイルの変更]に設定し、検索する文字列を入力します。この方法には、上記のコマンドと比較して次の制限があります。

  1. Sourcetree は、変更されたファイルの1つに検索語を含むコミットのみを表示します。検索テキストを含む正確なファイルを見つけることも、やはり手作業です。
  2. RegExはサポートされていません。

4

私があなたのところにいるときはいつでも、次のコマンドラインを使用します。

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

説明:

  1. git log-ここにもっと書く必要があります。ログは時系列で表示されます。
  2. -S "<words/phrases i am trying to find>" -これは、任意のファイル(追加/変更/削除)に「<>」記号なしで検索しようとしている単語/フレーズが含まれているすべてのGitコミットを示しています。
  3. --all -すべてのブランチを強制および検索します。
  4. --oneline -Gitログを1行で圧縮します。
  5. --graph -時系列に並べられたコミットのグラフを作成します。

1
「私があなたのところにいるときはいつでも、gitを使用する必要性を感じています!」
Sebi

1
これは素晴らしい答えです!
Alf Eaton

@AlfEaton私の喜び!
surajs1n

2

Jeetの答えはPowerShellで機能します。

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

以下は、コミットを含む、を含むすべてのファイルを表示しますpassword

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

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

1

それで、何かが最後に存在する場所を確認するために、古いバージョンのコードをgrepしようとしているのでしょうか。

もし私がこれをしているなら、私はおそらくgit bisectを使用するでしょう。bisectを使用して、既知の良好なバージョン、既知の不良バージョン、およびバージョンが良好か不良かを確認する単純なスクリプトを指定できます(この場合は、探しているコードが存在するかどうかを確認するためのgrepです)。 )。これを実行すると、コードがいつ削除されたかがわかります。


2
はい。ただし、「テスト」は、コードを確認して、コードが存在する場合は「true」を返し、存在しない場合は「false」を返すスクリプトにすることができます。
Rob Di Marco

2
さて、コードがリビジョン10で不良だった場合、リビジョン11で良好になり、リビジョン15で再び不良になるとしたら...
Paolo

2
私はパオロに同意します。バイナリ検索は、「順序付けられた」値にのみ適しています。git bisectの場合、これはすべての「良い」リビジョンがすべての「悪い」リビジョンの前に、参照ポイントから始まることを意味しますが、一時的なコードを探すときにその仮定を行うことはできません。このソリューションは場合によっては機能する可能性がありますが、適切な汎用ソリューションではありません。
ケント

ツリー全体がbisectで複数回チェックアウトされるため、これは非常に非効率的だと思います。
U.ウィンドル

0

シナリオ:IDEを使用してコードを大幅にクリーンアップしました。問題:IDEが必要以上にクリーンアップしたため、コードがコンパイルされません(リソースが不足しているなど)。

解決:

git grep --cached "text_to_find"

「text_to_find」が変更されたファイルを見つけます。

この変更を元に戻し、コードをコンパイルできます。


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

Jeetのソリューションを微調整したものです。そのため、検索が終了したときだけでなく、検索中に結果が表示されます(大規模なリポジトリでは時間がかかる場合があります)。


-1

私の場合、私は短いコミットを検索する必要があり、リストされた解決策は残念ながら機能していませんでした。

私はそれをなんとかして(REGEXトークンを置き換えます):

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
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.