gitを使ってどのコミットでバグが入り込んだか特定する。git bisectの使い方

git bisectは二分検索で問題があるコミットを探し出してくれるコマンド。

今回用意したリポジトリではあまり二分検索の良さが享受できないが、使い方のデモくらいはできると思う。

リポジトリ: https://github.com/newnakashima/git-learn

単純な使い方

まず、masterのHEADにいる状態で、git bisect startを実行する。

$ git bisect start

次に、HEADでテストを実行してみる。

$ node test.js
hogehoge

assert.js:49
  throw new AssertionError(obj);
  ^

AssertionError [ERR_ASSERTION]: 'hogehoge\n' === 'fugafuga\n'
    at Socket.<anonymous> (/Users/nakashima/develop/git-learn/test.js:10:16)
    at Socket.emit (events.js:180:13)
    at addChunk (_stream_readable.js:274:12)
    at readableAddChunk (_stream_readable.js:261:11)
    at Socket.Readable.push (_stream_readable.js:218:10)
    at Pipe.onread (net.js:581:20)

テストが通らなかったので、git bisect badコマンドで、HEADのコミットに問題が含まれていることをgitに教える。

$ git bisect bad

次に、テストが通っていたことが確認できているコミットIDを指定してgit bisect goodコマンドを実行して、問題なかったコミットをgitに教える。この場合はinitial commitのIDを渡している。

$ git bisect good 
Bisecting: 1 revision left to test after this (roughly 1 step)
[d27eedce7b2668a995dc0e73560db6f66edb8ed7] hogehogeに戻した

すると、未検証のコミットに自動でチェックアウトしてくれる。ここで再度テストを実行し、gitに結果を教える。

$ node test.js
assert.js:49
  throw new AssertionError(obj);
  ^

AssertionError [ERR_ASSERTION]: 'hogehoge\n' === 'fugafuga\n'
    at Socket.<anonymous> (/Users/nakashima/develop/git-learn/test.js:9:16)
    at Socket.emit (events.js:180:13)
    at addChunk (_stream_readable.js:274:12)
    at readableAddChunk (_stream_readable.js:261:11)
    at Socket.Readable.push (_stream_readable.js:218:10)
    at Pipe.onread (net.js:581:20)

$ git bisect bad
Bisecting: 0 revisions left to test after this (roughly 0 steps)
[10c88efbec451262196cd451ee52618b660c397b] fugafugaに変更

再度gitが未検証のコミットにチェックアウトしてくれるので、テストを実行して結果を教える。テストが通った場合はgit bisect goodを実行する。

これを続けていくと、最終的にどこでバグが混入したのかgitが教えてくれる。

$ node test.js
test OK
$ git bisect good
d27eedce7b2668a995dc0e73560db6f66edb8ed7 is the first bad commit
commit d27eedce7b2668a995dc0e73560db6f66edb8ed7
Author: newnakashima <newnakashima2014@gmail.com>
Date:   Tue Jul 24 00:56:04 2018 +0900

    hogehogeに戻した

:100644 100644 a31254a12816398ce382e6096d7923f330a9ac17 8aa758ae91689ffa39dc30412735a7ddb071032b M    app.js

問題のコミットがわかったら、git bisect resetをして、git bisectの状態を元に戻しておく。

$ git bisect reset
Previous HEAD position was 10c88ef... fugafugaに変更
Switched to branch 'master'
Your branch is up to date with 'origin/master'.

git bisect に自動でテストをやってもらう方法

コミットが多いと毎回手動でテストを実行するのがだるい。git bisectは自動でテストを実行して問題のコミットを見つけてくれたりもする。

git bisect startコマンドには検索開始コミットと終了コミットを指定できる。

$ git bisect start HEAD 8cc2236519e3919bf9edbcd937fd3c0f464dfb04
Bisecting: 1 revision left to test after this (roughly 1 step)
[d27eedce7b2668a995dc0e73560db6f66edb8ed7] hogehogeに戻した

自動でテストを回してもらうには、git bisect runコマンドを使う。

$ git bisect run node test.js
running node test.js
assert.js:49
  throw new AssertionError(obj);
  ^

AssertionError [ERR_ASSERTION]: 'hogehoge\n' === 'fugafuga\n'
    at Socket.<anonymous> (/Users/nakashima/develop/git-learn/test.js:9:16)
    at Socket.emit (events.js:180:13)
    at addChunk (_stream_readable.js:274:12)
    at readableAddChunk (_stream_readable.js:261:11)
    at Socket.Readable.push (_stream_readable.js:218:10)
    at Pipe.onread (net.js:581:20)
Bisecting: 0 revisions left to test after this (roughly 0 steps)
[10c88efbec451262196cd451ee52618b660c397b] fugafugaに変更
running node test.js
test OK
d27eedce7b2668a995dc0e73560db6f66edb8ed7 is the first bad commit
commit d27eedce7b2668a995dc0e73560db6f66edb8ed7
Author: newnakashima <newnakashima2014@gmail.com>
Date:   Tue Jul 24 00:56:04 2018 +0900

    hogehogeに戻した

:100644 100644 a31254a12816398ce382e6096d7923f330a9ac17 8aa758ae91689ffa39dc30412735a7ddb071032b M    app.js
bisect run success

手動でやるのと同じ手順をgitが勝手にやってくれた。便利。

git bisect runで指定したコマンドが成功したのか失敗したのかの判定は、コマンドの終了ステータスで判定している。0のときは成功、1 - 127(125除く)のときは失敗、と判定されるらしい。125のときはテスト不能と判定され、スキップされるらしい。

参考

git-bisect(1)

www.ohmsha.co.jp