Claude Codeでgit履歴から経緯を調べる — log -Sとblameで出典付きの答えに
古いコードの分岐がいつ・なぜ入ったかを、git log -S・-L・blame -wで追わせる手順です。コミットhashとPR番号を付けさせ、履歴に理由が無いときは推測と書き分けさせる運用までまとめます。
古いコードの「なぜこうなっているか」は、現在のコードを読んでも出てきません。答えは変更が入った時点のコミットにあります。Claude Codeにはその一点を追わせ、答えにはコミットhash・日付・PR番号を必ず付けさせるのが近道です。
追う道具は3つです。git log -Sで「その文字列が入った瞬間」を探し、git log -Lで関数単位の変更履歴を出し、git blame -wで行ごとの最終変更を見ます。読み取りだけのgitは確認なしで走るので、調べている間は承認待ちがほとんど挟まりません。
読み取りのgitは確認なしで走る — その範囲
permissionsのページによると、Claude Codeは組み込みの「読み取り専用コマンド」を、権限の確認なしで全モードで実行します。一覧にはls・cat・grep・find・diffなどと並んで「read-onlyな形のgit」が入っています。この集合は設定で変えられず、確認を挟みたいコマンドにはaskかdenyのルールを足します。
どのgitサブコマンドが読み取り専用の形に入るかの一覧は、このページには載っていません。log・blame・show・diffが通るかは、手元で試して確かめるのが確実です。
読み取り専用でも、確認が戻る条件がManualモードにあります。履歴調査に関係するものは3つです。
- クォートなしのglob:
git log -- src/*.pyのように*を裸で書くと確認が出ます。globが-deleteのようなフラグに展開されることがあるためです。パスは引用符で囲むか、ディレクトリ指定にします cdとgitの組み合わせ: 別ディレクトリへcdしてからgitを走らせると確認が出ます。そのディレクトリのフックが動く可能性があるためです。リポジトリの根で起動すれば避けられます- 解析できないコマンド: 構文を解析しきれないコマンドや、1万字を超えるコマンドは読み取り扱いにならず、確認が出ます
プランモードでは、ソースを編集せず読み取り専用のシェルコマンドで調べる動きになります。「経緯だけ知りたい」ときにプランモードで起動すると、調査が書き込みに流れる心配も減ります。
許可ルールを足す場合の注意もあります。同ページのワイルドカードの表には、Bash(git log * main)がgit log --output=<file> mainにも一致する例が載っています。--outputは結果をファイルへ書き出すオプションなので、git log *を広く許可すると「読み取りのつもりが書き込みを通してしまう」ルールになります。組み込みの読み取り専用扱いで足りるうちは、許可ルールを増やさないほうが素直です。
3つのコマンドの使い分け
知りたいことで道具が変わります。
| 知りたいこと | コマンド | 返ってくるもの |
|---|---|---|
| ある文字列・条件式が入った(消えた)のはいつか | コマンドgit log -S'文字列' | 返ってくるものその文字列の出現数が変わったコミット |
| 関数全体が時間とともにどう変わったか | コマンドgit log -L:関数名:ファイル | 返ってくるもの関数に触れたコミットごとの差分 |
| いま見えている行を最後に変えたのは誰か | コマンドgit blame -w -C ファイル | 返ってくるもの行ごとの最終コミット |
| そのコミットに理由は書いてあるか | コマンドgit show コミット | 返ってくるものメッセージ全文と差分 |
以下は、手元に作った小さなサンプルリポジトリでの実行結果です(git 2.50.1)。calc_feeにplan == "legacy"の分岐が入り、あとから整形コミットが2回入った履歴にしてあります。
6c96241 style: 空行とコメントを整理 (#140)
8ba235f style: インデントを2スペースに統一 (#150)
11cc12d fix: legacyプランは定額100円に (#128)
b0a2c48 add: 手数料計算を追加log -Sは「入った瞬間」を一発で絞る
-Sは、指定した文字列の出現数が変わったコミットだけを返します。整形コミットは文字列の数を変えないので、結果に混ざりません。
git log -S'"legacy"' --oneline -- src/billing.py11cc12d fix: legacyプランは定額100円に (#128)比べにgit log -G'legacy'(正規表現に一致する行が増減したコミット)を実行すると、整形でコメントを消した#140のコミットも返りました。-Sは「入った・消えた」、-Gは「その語を含む行を触った」という違いがあります。入った起点だけを知りたいなら-Sが向いています。
log -Lは関数の履歴を差分つきで出す
git log -L:calc_fee:src/billing.pyは、calc_feeの範囲に触れたコミットだけを、その範囲の差分とともに新しい順に出します。サンプルでは#150と#140の整形、#128の分岐追加、最初の追加の4件が並びました。関数が大きく育つ過程を、時系列で読ませたいときに向いています。
関数名の検出は拡張子ごとのルールに頼ります。Pythonのこの例では名前だけで通りましたが、検出に失敗する言語では-L 10,40:ファイルのように行範囲で渡します。
showでメッセージ本文を読む
-Sや-Lで候補が絞れたら、git show --stat <hash>でメッセージ全文を読みます。サンプルの11cc12dには、本文に「旧契約の請求書と金額が合わない問い合わせ(CS-512)への対応」とありました。「なぜ」の答えはここにあります。件名だけの履歴では、答えが出ません。
blameは整形コミットに騙される
git blameは行ごとに最後に触ったコミットを返します。インデントだけを変えるコミットがあると、その行の「最後に触った人」は整形コミットになります。
落とし穴
整形コミットが分岐の本当の出自を隠す
サンプルで#150(インデント統一)のあとに素のgit blame -sを実行すると、legacyの分岐の行はすべて8ba235f(整形コミット)と表示されました。-wを付けると空白の違いが無視され、同じ行が11cc12d(#128)に戻りました。--ignore-revで整形コミットのhashを指定しても同じ結果です。
Claude Codeに追わせるときは、blameの結果をそのまま答えにさせないのがコツです。「blameで出たコミットが整形・移動だけなら、-w -Cを付けて辿り直し、実際の変更コミットを答える」と指示します。-Cは別ファイルから移動・コピーされた行を追う指定です。サンプルは同一ファイル内の変更だけなので、-Cの効果までは再現していません。
ファイルが移動・改名されていると起点がずれる
経緯をたどる途中でよく起きるのが、ファイルの移動です。サンプルにsrc/billing.pyをsrc/fees/calc.pyへ移すコミット(a094084、#160)を足して試すと、パスを絞った検索の答えが変わりました。
git log -S'"legacy"' --oneline -- src/fees/calc.py # 現在のパスに絞る
git log -S'"legacy"' --oneline # パスを絞らない
git log --follow --oneline -- src/fees/calc.py # 改名を越えて追うa094084 refactor: billing を fees/calc に移動 (#160)
---
11cc12d fix: legacyプランは定額100円に (#128)
---
a094084 refactor: billing を fees/calc に移動 (#160)
8ba235f style: インデントを2スペースに統一 (#150)
6c96241 style: 空行とコメントを整理 (#140)
11cc12d fix: legacyプランは定額100円に (#128)
b0a2c48 add: 手数料計算を追加現在のパスに絞った-Sは、そのパスにファイルが現れた移動コミットを「入った瞬間」として返します。パスを絞らなければ本当の起点の11cc12dが出ます。--followは単一ファイルの履歴を改名越しにたどり、移動前の5件まで出しました。git blameはファイル単位の改名を既定で追うので、-Cを付けなくても改名を越えて元のコミットに戻れます(行の左に移動前のファイル名が出ます)。
ここから、依頼文に入れる決まりが2つ出てきます。1つ目は、-Sでパスを絞るのは最後にして、まず全体を検索させること。2つ目は、結果が「移動」「改名」「一括整形」のコミットなら、それを起点と答えず、さらに1段さかのぼらせることです。リファクタリングのコミットは、CLAUDE.mdの型に「起点が移動・整形コミットなら遡る」と一行足すだけで拾えます。
履歴が長いリポジトリで読ませる量を絞る
数年分の履歴を持つリポジトリでgit log -pを素のまま走らせると、出力が膨らんで、肝心の1コミットを読む前にコンテキストを食います。調べ方は2段にします。
- 最初は
--onelineで、コミット件名とhashだけを出させる。候補が10件以下になるまで、-Sの語・パス・--sinceで絞る - 絞れたコミットだけを
git show --stat <hash>で開き、メッセージ本文と変更ファイルを読む。差分が必要になったら、ファイルを指定してgit show <hash> -- <パス>にする
この順に直すだけで、依頼文は「候補を出す」と「候補を読む」の2つに分かれます。先に件名で絞れば、読み違いも減ります。サンプルでも、-Sの結果が1件に絞れた時点で、メッセージ全文を読めば足りました。
絞り込みに使う語は、定数名・設定キー・エラーメッセージの文字列のように、コードの中で変わりにくい語が向いています。変数名は途中で改名されがちで、-Sが起点を取りこぼします。依頼文の最初に「この分岐を特徴づける文字列」を指定しておくと、検索の精度が上がります。
答えの型を決めておく
履歴調査で怖いのは、コミットメッセージに無い理由を、もっともらしく補って答えてしまうことです。型を先に決めておくと防げます。1つは、毎回の依頼文に書く方法です。
src/billing.py の calc_fee にある plan == "legacy" の分岐は、いつ・なぜ入った?
git log -S で入ったコミットを特定し、git show でメッセージ本文を読んで答えて。
答えには短縮hash・日付・PR番号を付けて。
理由がメッセージに書かれていなければ、記録と推測を分けて書いて。繰り返し使うならCLAUDE.mdに書いておきます。
## コードの経緯を聞かれたとき
- 現在のコードを読む前に git log -S / -L で変更が入ったコミットを特定する
- 答えには短縮hash・日付・コミットメッセージの引用を付ける
- PR番号がメッセージにあれば併記し、無ければ「PR番号は履歴から特定できない」と書く
- メッセージに書かれた理由は「記録」、読み取った意図は「推測」と見出しを分ける
- blameが整形コミットを指したら -w で辿り直す
- 起点が移動・改名・一括整形のコミットなら、パスを絞らずに -S を再実行するか --follow で1段さかのぼるこの型で返ってくる答えは、たとえば次の形になります(サンプルの履歴から人が書いた例です。日付はgit log --date=shortの値です)。
記録: 11cc12d(2026-04-10)「fix: legacyプランは定額100円に (#128)」
本文に「旧契約の請求書と金額が合わない問い合わせ(CS-512)への対応」とある。
推測: コメントの "legacy plan is flat" は #140 で削除されている。
以降は分岐の意図を示す文書がコード内に残っていない。記録の行は、そのままコミットを開けば検証できます。推測の行は、検証できないことが見出しで分かります。レビューで「この説明の根拠は?」と聞かれたとき、hashを渡せる状態になるのが狙いです。
履歴に理由が無いとき
メッセージが「fix」だけ、あるいは理由が別の場所にあるケースもあります。履歴からは次の順で広げます。
- 同じコミットの前後を
git log --oneline -5 <hash>で見て、関連する作業の流れを探す - メッセージにPR番号があれば、PRの説明とレビューコメントに理由が残っていないか見る。PRの参照にはGitHubのCLIが要り、permissionsのページが挙げる読み取り専用コマンドには名前がありません。許可するなら
Bash(gh pr view *)のように範囲を絞ったルールを足します - スカッシュマージの履歴では、元の細かいコミットが残らず、メッセージも1本にまとまっています。その場合はPRの説明が唯一の出典になります
それでも理由が出なければ、答えは「履歴からは特定できない。推測するなら〜」で終わらせます。そこで推測を事実のように書かせないことが、この運用の肝です。
隣接する調べ方との違い
回帰が入ったコミットを探すなら、二分探索で特定するgit bisectの手順が向いています。bisectは「いつから壊れたか」に、この記事の手順は「なぜそう書かれたか」に答えます。
現在のコードの構造を先に把握したいときは、大規模コードベースの探索のコツが出発点です。変更が集中している場所を先に探すなら、変更回数でホットスポットを洗い出す方法が使えます。
まとめ
経緯の調査は、探す(-S・-L)、読む(show)、確かめる(blame -w)の3動作に分けると、Claude Codeに任せやすくなります。答えにhashとPR番号を付けさせ、記録と推測を分けさせれば、読んだ人が自分で検証できる報告になります。
整形コミットが多いリポジトリほど、blameの素の結果は当てになりません。その事実を型に入れておくだけで、「なぜ」の答えの信頼度が変わります。
参照した一次ソース
この記事の記述は、次の一次ソースと照合しています。更新時に参照先が変わった場合は一覧も更新します。