Gitの小ネタおれおれAdvent Calendar 2022 – 24 日目

長くやって機能が増えてくると「なんか気づいたらアレが動かなくなってた」「知らんうちにバグってた」みたいな経験があると思います。さっさと修正すればいい、それはそうなんですが、どの変更の影響を受けたのかを調べると修正が簡単になることがしばしばあります。(注視すべき箇所がわかるので。)

そんなときに便利なのが git bisect というコマンドです。二分探索のようにコミットを前後に飛び回りながら、問題のコミットを探し出すことができます。

ちなみに bisect とは「二等分する」という意味の動詞。(ちなみにちなみに「二分探索」は普通 binary search です。)

先にまとめ

  • Git の履歴を飛び回りながら「大丈夫」「駄目」をマークしてゆく
  • いわゆる二分探索
  • 1000 コミットの範囲なら 10 回で見つけ出せる
  • git bisect start
  • git bisect bad
  • git bisect good
  • git bisect reset

やり方

  1. git bisect start で開始 …(A)
  2. git checkout @{last.week} とかで戻る
  3. 動作確認(画面触ったり試験回したり)
  4. 大丈夫なら git bisect good 、駄目なら git bisect bad …(B)
  5. 自動で (A) と (B) の中間のコミットへ移動してくれる
  6. ここでも動作確認して大丈夫かどうか確認。繰り返す
  7. good と bad の境を見つけると教えてくれる
  8. git bisect reset で終了。最初の位置 (A) へ戻してくれる

二分探索物語

例です。

あらすじ

例えばこんなコミット状況だったとします。

* commit-9 (いまここ)
* commit-8
* commit-7
* commit-6
* commit-5
* commit-4
* commit-3
* commit-2
* commit-1 (1 週間前)

何かの作業を commit-9 としてコミットしたところで、それとは関係ない不具合を発見しました。記憶が確かなら 1 週間前は正常に動いていたはずです。現在とそことの間のどこかで何かが間違ってしまったようです。

ここで git-bisect の出番です。

二分探索の開始

まずは探索モードに入ります。(ちなみに終了は bit bisect reset です。)

$ git bisect start

続いて現在の位置ではうまくうごいていないことを Git ちゃんに伝えておきます。まだ何も起こりません。

$ git bisect bad

次は、記憶を頼りにまだ動いていたはずの 1 週間前のコミットへ移動します。"@{last week}" で 1 週間前を指せます。(その他 "@{yesterday}" とか "@{3 months ago}" とかでも可。)

$ git checkout "@{last week}"

これで今 commit-1 にいます。

この状態でまあビルドなり何なりして、動作確認します。どうやら記憶通り正常に動作していました。

これも Git ちゃんへ知らせます。OK なので git bisect good です。すると知らせを受けた Git ちゃんは、good と bad のちょうど中間のコミットへ自動的に移動してくれます。(かしこい。)

$ git bisect good
Bisecting: 535 revisions left to test after this (roughly 9 steps)
[1234567890abcdef1234567890abcdef12345678] commit-5
* commit-9 <- 1. `git bisect start`, `git bisect bad`
* commit-8
* commit-7
* commit-6
* commit-5       <- 3. いまここ
* commit-4
* commit-3
* commit-2
* commit-1    <- 2. `git bisect good`

見つけるまで繰り返し

というわけで同じくこの commit-5 でも動作確認をします。正常に動作しました。じゃあ最近なんだな。good のフラグを立てます。

bad と good の中間は今度は commit-7 となるので、Git ちゃんがそこへ移動してくれます。(えらい。)

* commit-9 <- 1. `git bisect start`, `git bisect bad`
* commit-8
* commit-7          <- 4. いまここ
* commit-6
* commit-5       <- 3. `git bisect good`
* commit-4
* commit-3
* commit-2
* commit-1    <- 2. `git bisect good`

同様に動作確認を行い、駄目だったので bad のフラグを立てます。

次はもちろんcommit-6 です。動作確認を繰り返します。駄目です。bad のフラグを立てます。

これで commit-5 は good 、commit-6 は bad ですから、壊れたのはこの commit-6 であることがわかります。

* commit-9 <- 1. `git bisect start`, `git bisect bad`
* commit-8
* commit-7          <- 4. `git bisect bad`
* commit-6             <- 5. `git bisect bad`
* commit-5       <- 3. `git bisect good`
* commit-4
* commit-3
* commit-2
* commit-1    <- 2. `git bisect good`

Git ちゃんに bad と伝えるとそれを教えてくれます。

$ git bisect bad
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx is the first bad commit
commit xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Author: ginpei <[email protected]>
Date:   Fri Jan 1 00:00:00 2000 +0000

    commit-6

 src/something/important.ts | 1 +
 1 file changed, 1 insertion(+)

めでたしめでたし、の前にお片付けをお忘れなく。

後始末

問題のあるコミットが判明したので、今後はこのコミットを revert なり確認して手動で修正するなりします。コミット ID を覚えておいてください。

修正作業に入る前に、git bisect モードを終了します。

$ git bisect reset
Previous HEAD position was 2abf267 remove unused paragraph
Switched to branch 'main'

元のコミットへ戻してくれます。では落ち着いて修正を開始しましょう。

おしまい

検索回数が対数になるので効率がとても良い。便利~。

参考