何が起きてこうなったのか
運用しているコードの調査をする際などに、 「このコードはどういう経緯を経てこのようになったのだろう?」 と疑問に抱くことは多々あるかと思います。
この時 git blame を使えば効率的に特定のコードブロックの変更を追うことができます。
この文書では、 git blame を用いて、特定にメソッド変更履歴を追う方法を解説します。
説明のために以下のようなコードがあるとしてください。
- 1000 行くらいある ruby のファイル hoge.rb がある
- 中に、 7 行くらいある
target_method
というメソッドが定義されている target_method
はかれこれ 5 回くらい書き換えられている
盛大なネタバレ
このドキュメント無視して tig blame
を使うといいよ!
簡単な使い方
git blame はファイルの行ごとに、最後に変更されたリビジョンを表示するコマンドです。
$ git blame hoge.rb
6d305b5190c (Bob 2015-04-07 15:28:07 +0900 1) def foo
6d305b5190c (Bob 2015-04-07 15:28:07 +0900 2) bar = 1
191b9234aea (Alice 2017-01-12 18:13:36 +0900 3) baz = 2
dae880d329d (Ted 2015-04-15 16:08:27 +0900 4) (bar + baz).times do
dae880d329d (Ted 2015-04-15 16:08:27 +0900 5) puts "!"
191b9234aea (Alice 2017-01-12 18:13:36 +0900 6) end
dae880d329d (Ted 2015-04-15 16:08:27 +0900 7) end
表示内容は左側から、その行を最後に更新したコミットの SHA-1 の一部、コミッター、コミット日時、行番号、そしてファイルの内容です。
オプション -L
で表示範囲を絞り込む
デフォルトではファイル全部の中身を表示するので、実運用上は、 -L
オプションを用いて表示内容を絞り込むと便利です。
-L <start>,<end>, -L :<funcname>
与えられた範囲のみを注釈して表示します。複数回オプションが指定された場合は可能な限り範囲を重複させて表示します。
ここで <start>
と <end>
は両方共オプショナルです。ですので、 -L <start>
, -L <start>,
, -L ,<end>
はそれぞれ
「<start>
からファイル末尾まで」「<start>
からファイル末尾まで」「ファイル先頭から <end>
まで」と正しく解釈されます。
<start>
と <end>
は次の3つの値をとることができます。
数値を指定する
<start>
または <end>
には数値を指定することができます。この数値は行番号とみなされます。
$ git blame -L 10,24 app/models/user.rb
正規表現を指定する
POSIXの正規表現を指定することができます。
<start>
に正規表現を指定した場合、前の -L
の範囲の終わりから検索をし、マッチする行を表示します。
前に -L
が設定されていないなら、ファイルの初めから検索をします。
<end>
に正規表現をs指定した場合は、<start>
から検索をし、マッチする行までを表示します。
$ git blame -L "/def display_name/","/end/" app/models/user.rb
オフセットを指定する
<end>
にのみ指定でき、<start>
から初めて何行表示するかを指定できます。
$ git blame -L "/def display_name/",+30 app/models/user.rb
関数名を指定する
<funcname>
を指定すると、その関数の定義部のみが表示されます。
C言語のみかと思います。少なくとも ruby のコードでは動きませんでした。
$ git blame -L :blame_origin blame.c
この辺のオプションを駆使して、表示範囲を絞り込んでみてみましょう。
例えば、 target_method
の定義部分だけをみたいのであれば、こんなオプションが良いでしょう。
$ git blame -L "/def target_method/","/^end/" foo.rb
6d305b5190c (Bob 2015-04-07 15:28:07 +0900 247) def target_method
6d305b5190c (Bob 2015-04-07 15:28:07 +0900 248) bar = 1
191b9234aea (Alice 2017-01-12 18:13:36 +0900 249) baz = 2
dae880d329d (Ted 2015-04-15 16:08:27 +0900 250) (bar + baz).times do
dae880d329d (Ted 2015-04-15 16:08:27 +0900 251) puts "!"
191b9234aea (Alice 2017-01-12 18:13:36 +0900 252) end
dae880d329d (Ted 2015-04-15 16:08:27 +0900 253) end
引数 <rev>
で blame のもとなるリビジョンを指定する
最新版のソースから blame していても、歴史を追うことはできません。
でも、心配いりません。 git blame <rev> file
することで、 blame
するソースコードのリビジョンを指定することができます。
先ほどの target_method
で絞り込んだ blame 結果をもう一度みてみましょう。
$ git blame -L "/def target_method/","/^end/" foo.rb
6d305b5190c (Bob 2015-04-07 15:28:07 +0900 247) def target_method
6d305b5190c (Bob 2015-04-07 15:28:07 +0900 248) bar = 1
191b9234aea (Alice 2017-01-12 18:13:36 +0900 249) baz = 2
dae880d329d (Ted 2015-04-15 16:08:27 +0900 250) (bar + baz).times do
dae880d329d (Ted 2015-04-15 16:08:27 +0900 251) puts "!"
191b9234aea (Alice 2017-01-12 18:13:36 +0900 252) end
dae880d329d (Ted 2015-04-15 16:08:27 +0900 253) end
この中で、最も新しいのは、 2017/01/12 にコミットされた 191b9234aea だとわかります。
ですので、1つ前のコミットをもとに blame すると、191b9234aea が適用される前の状態での blame 結果がわかります。
$ git blame -L "/def target_method/","/^end/" 191b9234aea^1 foo.rb
6d305b5190c (Bob 2015-04-07 15:28:07 +0900 247) def target_method
6d305b5190c (Bob 2015-04-07 15:28:07 +0900 248) bar = 1
ibe7a4b882d (Carol 2016-12-24 18:13:36 +0900 249) baz = @global_val
dae880d329d (Ted 2015-04-15 16:08:27 +0900 250) (bar + baz).times do
dae880d329d (Ted 2015-04-15 16:08:27 +0900 251) puts "!"
ibe7a4b882d (Carol 2016-12-24 18:13:36 +0900 249) end if baz > 2
dae880d329d (Ted 2015-04-15 16:08:27 +0900 253) end
これを繰り返すことで、歴史を延々と遡れますね!
辿りやすくしよう
毎回このコマンドを手打ちすると考えるとバカバカしく感じます。
そんなわけで、使い捨てのスクリプトを書いておきましょう。
これで辿りやすくなりましたね!
車輪の再開発乙。こういうのはだいたい先人が作っているものだって。
ここまで、わざわざいろいろ調べて自作しましたが、この手のツールは大抵の場合は誰かが実装しています。
vcs-ann は git または svn に対応した blame 結果のブラウジングツールです。便利。
あと、そもそも tig を普段使いしている人であれば、 tig blame
すれば十分に履歴をたどれます。
やっぱり世界は広いですね。