1. ホーム
  2. 記事一覧
  3. Gitコマンドでバグが混入したコミットを見つける!便利な「git bisect」コマンドを徹底解説

2024.05.27

Gitコマンドでバグが混入したコミットを見つける!便利な「git bisect」コマンドを徹底解説

アプリ開発において「Git」は必須スキルとなってきます。Gitの基本コマンドは知っているけど他のコマンドはよく知らない、Gitコマンドをもっと知って便利に使いたい!と思っている方、また バグの原因となったコミットが何なのか最速で知りたい! という方向けに、この記事では「git bisect」コマンドについて解説します。

GitについてはエンベーダーのLinux応用コースで詳しく学ぶことができます。

https://envader.plus/course/5/scenario/1055

様々なGitコマンドを習得し、Gitマスターを目指しましょう!

「git bisect」とは

git bisectコマンドは、Git のバージョン管理システムに組み込まれたデバッグツールです。「bisect」とは英語で二等分することを意味します。このコマンドでは二分探索アルゴリズムを使用することで、バグが混入したコミットを効率的に特定することができます。

「git bisect」でできること

コマンドを実行すると、以下のようなことが可能です。

  • 二分探索アルゴリズムを使用して、最小限のステップでバグの原因となるコミットを見つける
  • ファイルの実行やテストコードを用いて自動化することで、大規模なリポジトリでも容易にバグの原因特定をする

二分探索アルゴリズムとは

要素の集合から特定の要素を効率的に見つけ出すためのアルゴリズムで、基本的な流れは以下の通りです。

  1. 探索範囲を決定し、その中央の要素を選択する
  2. 選択した要素が探索対象と一致するかを確認する
    • 一致する場合、探索を終了する
    • 一致しない場合、探索対象が選択した要素より大きいか小さいかを判断する
  3. 探索対象が選択した要素より大きい場合、探索範囲を選択した要素より大きい部分に絞り込む
  4. 探索対象が選択した要素より小さい場合、探索範囲を選択した要素より小さい部分に絞り込む
  5. 手順1~4を繰り返し、探索対象が見つかるまで続ける

「git bisect」における二分探索アルゴリズム

二分探索をコミット履歴に適用することで、バグが混入したコミットを効率的に特定します。

  1. まず、バグが発生していない古いコミットと、バグが発生している新しいコミットを指定する(バグが混入したコミットは、この2つのコミットの間に存在する)
  2. 指定すると、その古いコミットと新しいコミットの中間にあたるコミットをGitが自動的に選択してチェックアウトをする
  3. そのコミットで、バグが発生するかどうかをテストする
    • バグが発生しない場合、そのコミットより新しいコミットでバグが混入したことになるため、探索範囲が自動的に選択したコミットより新しい側に絞り込まれ、その範囲の中間のコミットに自動でチェックアウトされる
    • もしバグが発生する場合、それより古い側に探索範囲が絞り込まれ、自動的に中間のコミットにチェックアウトされる
  4. 自動的にチェックアウトされるコミットでテストを繰り返すことで、バグが混入したコミットがGitにより特定される

「git bisect」の使い方

ここからは具体的な使い方を紹介いきます。git bisectコマンドでは、主に以下のようなサブコマンドを組み合わせて使用していきます。

  • start
  • bad
  • good
  • reset
  • view
  • log
  • run

サブブコマンドとはメインとなるコマンドの様々な機能を実行するためのコマンドで、メインコマンドの後にスペースを挟んで入力して使います。サブコマンドを使用することで、コマンドの機能を細分化し、より具体的な操作を行うことができます。

基本的にはチェックアウトしているコミットが「good(バグのない状態)」か「bad(バグがある状態)」かを1つずつ判定しそれをgit bisectコマンドに伝えてあげることで、二分探索を利用して選定されたコミットに自動的にチェックアウトされるため、そのコミットでまたテストを行っていく流れになっています。

基本的な使い方

developブランチが以下のような状況だったとし、git bisectコマンドを実施してみましょう。今回はわかりやすくコミットIDは最も古いものを「a111..」とし、コミットメッセージにはバグが混入しているかどうかが登録されているとします。

状況を確認するため、git logコマンドを実施した結果はこのようになっています。

commit e555555555555555555555555555555555555555 (HEAD -> develop)
Author: Fred <fred@example.com>
Date:   Thu May 16 06:36:39 2024 +0000

    バグのある状態

commit d444444444444444444444444444444444444444
Author: Fred <fred@example.com>
Date:   Thu May 16 06:36:26 2024 +0000

    バグのある状態

commit c333333333333333333333333333333333333333
Author: Fred <fred@example.com>
Date:   Thu May 16 06:36:13 2024 +0000

    バグのある状態

commit b222222222222222222222222222222222222222
Author: Fred <fred@example.com>
Date:   Thu May 16 06:35:34 2024 +0000

    バグが混入!!

commit a111111111111111111111111111111111111111
Author: Fred <fred@example.com>
Date:   Thu May 16 06:33:13 2024 +0000

    正常な状態

まずはgit bisect startコマンドで「git bisect」のモードを開始します。

git bisect start

開始するとターミナルの設定によってはプロンプトに「bisect」と表示されます。何も表示されない場合はgit bisect logコマンドを実行してみましょう。過去にgit bisectで利用したコマンドが出力されるため、きちんと開始されていれば実行したgit bisect startコマンドが表示されているはずです。もし正常に開始されていない場合、下記のようなエラーメッセージが表示されます。

git bisect start
git bisect log

# 開始されている場合、実施したスタートコマンドとメッセージが表示される
git bisect start

# 開始されていない場合、下記のエラーメッセージが表示される
We are not bisecting.

「git bisect」のモードを開始できたら、バグのないコミットとバグがある状態のコミットをそれぞれ1つ提示する必要があります。git bisect startが実行できたら、まず現状わかっているコミットの情報を伝えましょう。それにはgoodbadというサブコマンドを使います。

チェックアウトしているコミットの状態を伝える場合、コミットIDの指定は不要です。

# チェックアウトしているコミット(e555)にはバグがあることを伝える
git bisect bad
# badの指定の次に、現時点でバグがないとわかっているコミットを伝える
git bisect good a111

実施すると下のように表示され、自動で二分探索により真ん中のコミットである「c333…」のコミットにチェックアウトされます。

Bisecting: 1 revision left to test after this (roughly 1 step)
[c333333333333333333333333333333333333333] バグのある状態

表示されている「revision」というのはコミットと似たような意味を持っていて、この行では「c333…」コミットの状態を判定した後に残り何回判定することでバグが特定できるかを表示しています。(「c333…」を判定した結果がどちらだったにせよ、それぞれ「b222…」か「d444…」を残り1度判定することでバグの混入したコミットを特定できることを意味します。)

今回の例では「c333…」もバグのある状態のため、再度git bisect badコマンドを実行します。すると、判定する必要のある最後のコミットにチェックアウトされます。

git bisect bad
# 実行結果
Bisecting: 0 revisions left to test after this (roughly 0 steps)
[b222222222222222222222222222222222222222] バグが混入!!

コミットメッセージから今回はすでにわかっていますが、この「b222…」が今回バグの原因を混入したコミットです。バグがあるかどうかをテストなどで確認したら、再度git bisect badコマンドを実行しましょう。


git bisect bad

b222222222222222222222222222222222222222 is the first bad commit
commit b222222222222222222222222222222222222222
Author: Fred <fred@example.com>
Date:   Thu May 16 06:35:34 2024 +0000

    バグが混入!!

これで「コミットID is the first bad commit」というメッセージが表示され、バグの原因となったコミットを特定することができました。resetというサブコマンドを使うことで、最初にgit bisect startをした際チェックアウトしていたコミットに戻って「git bisect」のモードを終了することができます。

git bisect reset

その他のサブコマンドの使用方法

  • start

git bisect startを実施して開始する際に、最初の「bad」と「good」のコミットをそれぞれ指定することができます。例えば開始する際に以下のように指定することで、開始直後から二分探索を使って次に調査するべきコミットにチェックアウトさせることができます。

# HEADを「bad」に、a111を「good」に指定している
git bisect start HEAD a111

また、startサブコマンドを実施せずにいきなりgit bisect badを実施することも可能です。その場合、下記のようにstartを実施して「git bisect」のモードに入るかを聞かれるので、「y」とエンターを入力することで開始できます。

git bisect bad

You need to start by "git bisect start"
Do you want me to do it for you [Y/n]? 
  • log

git bisect startコマンドからこれまで判定した「good」と「bad」のコミット、そしてバグの原因となったコミットを既に特定している場合はそれを含めて一覧で表示します。先ほどの例でバグの原因となったコミットが見つかった後に実施した場合、以下のようになります。

git bisect log
# 以下、「#」から始まるコメント部分も含めて全てlogサブコマンドの実行結果

git bisect start
# bad: [e555555555555555555555555555555555555555] バグのある状態
git bisect bad e555555555555555555555555555555555555555
# good: [a111111111111111111111111111111111111111] 正常な状態
git bisect good a111111111111111111111111111111111111111
# bad: [c333333333333333333333333333333333333333] バグのある状態
git bisect bad c333333333333333333333333333333333333333
# bad: [b222222222222222222222222222222222222222] バグが混入!!
git bisect bad b222222222222222222222222222222222222222
# first bad commit: [b222222222222222222222222222222222222222] バグが混入!!
  • view

バグの原因となったコミットを特定している場合、そのコミットの詳細情報を改めて表示します。

git bisect view
    
commit b222222222222222222222222222222222222222 (HEAD, refs/bisect/bad)
Author: Fred <fred@example.com>
Date:   Thu May 16 06:35:34 2024 +0000

    バグが混入!!
  • run

git bisect runコマンドは、指定されたテストコマンドを実行し、その終了コードに基づいて自動的に「good」または「bad」の判定を行います。テストコマンドは、実行可能なファイルやシェルスクリプト、あるいはテストフレームワークを使用したテストコードなどを指定できます。既に「git bisect」モードに入っており、「good」と「bad」を最低1つずつ指定している必要があります。

単に一つのpythonファイルにバグがあるかどうかを確かめたい場合、以下のようにファイルを実行する形式のコマンドを入力することで自動的に終了コードに基づいてバグ判定が行われ、バグとなったコミットの特定が可能です。

git bisect run python3 script.py

終了コードとは?

Unix系のOSでは、プログラムが終了する際に終了コードが返されます。一般的に、終了コード0は正常終了を示し、1など0以外の終了コードはエラーや異常終了を示します。プログラムによっては独自の終了コードを定義している場合があります。

余談ですが、Linuxでは下記のコマンドで直前に実行したコマンドの終了ステータスを確認することができます。

echo $?

runサブコマンドでは、この仕組みを用いて自動で二分探索によるチェックアウトとテストコマンドの実行を順番に実施していき、0であれば「good」を、それ以外であれば「bad」の判定をしてバグが混入したコミットを特定します。

まとめ

git bisectは、バグが混入したコミットを効率的に特定するための強力なツールです。二分探索アルゴリズムを用いることで、最小限のステップでバグの原因となるコミットを見つけ出すことができるため、特に大きなプロジェクトで多くのコミットが存在する場合のバグ修正のプロセスを大幅に効率化できます。他の基本的なコマンドと違って複数のサブコマンドを使う必要はありますが、一度使ってしまえばとても簡単なツールなので、ぜひ使い方を身につけてご自身のプロジェクトや開発で活用してください。

【番外編】USBも知らなかった私が独学でプログラミングを勉強してGAFAに入社するまでの話

IT未経験者必見 USBも知らなかった私が独学でプログラミングを勉強してGAFAに入社するまでの話

プログラミング塾に半年通えば、一人前になれると思っているあなた。それ、勘違いですよ。「なぜ間違いなの?」「正しい勉強法とは何なの?」ITを学び始める全ての人に知って欲しい。そう思って書きました。是非読んでみてください。

「フリーランスエンジニア」

近年やっと世間に浸透した言葉だ。ひと昔まえ、終身雇用は当たり前で、大企業に就職することは一種のステータスだった。しかし、そんな時代も終わり「優秀な人材は転職する」ことが当たり前の時代となる。フリーランスエンジニアに高価値が付く現在、ネットを見ると「未経験でも年収400万以上」などと書いてある。これに釣られて、多くの人がフリーランスになろうとITの世界に入ってきている。私もその中の1人だ。数年前、USBも知らない状態からITの世界に没入し、そこから約2年間、毎日勉学を行なった。他人の何十倍も努力した。そして、企業研修やIT塾で数多くの受講生の指導経験も得た。そこで私は、伸びるエンジニアとそうでないエンジニアをたくさん見てきた。そして、稼げるエンジニア、稼げないエンジニアを見てきた。

「成功する人とそうでない人の違いは何か?」

私が出した答えは、「量産型エンジニアか否か」である。今のエンジニア市場には、量産型エンジニアが溢れている!!ここでの量産型エンジニアの定義は以下の通りである。

比較的簡単に学習可能なWebフレームワーク(WordPress, Rails)やPython等の知識はあるが、ITの基本概念を理解していないため、単調な作業しかこなすことができないエンジニアのこと。

多くの人がフリーランスエンジニアを目指す時代に中途半端な知識や技術力でこの世界に飛び込むと返って過酷な労働条件で働くことになる。そこで、エンジニアを目指すあなたがどう学習していくべきかを私の経験を交えて書こうと思った。続きはこちらから、、、、

note記事3000いいね超えの殿堂記事 今すぐ読む

エンベーダー編集部

エンベーダーは、ITスクールRareTECHのインフラ学習教材として誕生しました。 「遊びながらインフラエンジニアへ」をコンセプトに、インフラへの学習ハードルを下げるツールとして運営されています。

RareTECH 無料体験授業開催中! オンラインにて実施中! Top10%のエンジニアになる秘訣を伝授します! RareTECH講師への質疑応答可

関連記事