RevertとBlameを使いこなして安全性の高い開発を推進しよう

2017年3月14日(火)
池田 尚史

前回の記事ではProtected Branchesの機能を使ってシンプルなGitHub Flowに権限管理の味付けをする方法を学びました。

今回はPull Request単位でのRevertやビジュアルなBlame機能について学びましょう。

GitHub Flowに従ってmasterにマージする前のコードレビューやCIを必須にした運用をしていたとしても、誤ったコミットをマージしてしまい、システムテスト時または本番環境にて問題になることは、珍しい話ではありません。

そんな時に行いたいのがコミットのRevert(巻き戻し)ですが、Pull Requestをベースとした開発を行っていると、このような時にも恩恵をこうむることができます。

Revertとは

Revertとはコミットの巻き戻しのことです。たとえば次のようなコードをコミットしたとします。printの出力文字を変更しています。

- print("Hello world!")
+ print("こんにちはこんにちは")

このコードに問題があり、もとに戻したい時に行うのがRevertです。たとえば次のようなコミットを作り、元のコミットを打ち消すようなことをします。

- print("こんにちはこんにちは")
+ print("Hello world!")

この作業を補助する機能はGitだけでなく多くのバージョン管理システムに備わっていて、大抵の場合この機能の名前をRevertとしていることが多いです。Gitもその例に漏れません。例えば次のようなコマンドを実行することで、巻き戻しのコミットを作ることができます。これがRevertです。

$ git revert <コミットsha>

巻き戻したいコミットが1つの場合は特に難しいことはありませんが、もし複数のコミットに渡って巻き戻しをしたい場合にはどうなるでしょうか。

従来の場合

たとえば次のように、プロダクトの挙動を変更するコミットと、それに付随してテストコードの挙動を変えるコミットがあったとします。この例ではurl.jsというファイルにてデフォルトの都市名を変更していて、それに付随してテストコードであるurl_spec.jsを変更しています。

この要件をRevertしたい場合には、この2つのコミットを間違いなく巻き戻してやる必要があります。でないと、思わぬデグレードが発生する可能性が出てきます。

1人で開発している分にはこの巻き戻しも簡単です。この関連する2つのコミットを突き止めるのも容易ですし、間に無関係な変更が入っているということもありえません。

ところがソフトウェア開発は1人で行うものではありませんから、現実には、この2つのコミットの間に他人のコミットが入っていることがありえます。たとえば次のようなイメージです。

このようなケースの場合、ある特定の要件に係るコミットだけを正しく巻き戻そうとすると、コミット履歴を1つ1つ目で確認して、どのコミットを巻き戻すべきかを精査する必要が出てきます。ここで判断を誤ると、戻さなくても良いコミットまで戻してしまい、デグレードが起きる羽目になります。かと言って過去のある時点のコミットまですべて巻き戻してしまうと、必要のないコミットまですべて巻き戻ってしまいます。

Pull Requestを使っていると

Pull Requestベースで開発を行っていると、このようなケースに柔軟かつ迅速に対応できるようになります。

Pull Requestをマージすると、次のようにマージした履歴がPull Requestに表示されます。

この履歴の右端にRevertというボタンが有るのが分かるでしょうか。拡大すると次のようになります。

このボタンを押すと、Pull Request単位でRevertをすることができます。これはどういうことでしょうか。

先ほどの例でお見せしたように、ある要件に係るコミットをもれなく間違いなく巻き戻すためには、従来であれば目で確認する他ありませんでした。

Pull Requestを使って開発をしていると、この苦労をしなくて済むようになります。なぜならば、Pull Request自体にコミットが紐付けられているからです。

ですので、要件ごとにPull Requestを適切に利用して開発をしていれば、その巻き戻しはただRevertボタンを押すだけで良くなるのです。試しに押してみましょう。

すると、次のように新たに別のPull Requestが作られます。

このPull Request配下に作成されたコミットを見てみましょう。

先ほどのプロダクトコードとテストコードがともに巻き戻るようにコミットが作られているのがわかると思います。このように、Pull Requestベースで開発していると、その単位で巻き戻すのもこんなに簡単になります。

さらに、RevertコミットもPull Requestとして作られるので、この巻き戻しが本当に正しいかどうか、コードレビューとCIを実施できるというのも見逃せないメリットです。

Pull RequestをベースとしたGitHub Flowで開発していると、このように付随的なメリットもあることがお分かりいただけたかと思います。

特定のコード行の変更について調べる

Pull Requestを使っているとRevertも容易にできることがわかりました。

ソフトウェア開発をしていると、次のようなケースに出くわすことも多いかと思います。 たとえば、 − 障害が発生してしまい、原因追求をしないといけない − 調査の結果、特定のコードが原因であることがわかったが、なぜそのコードが作成されたかを知りたい

このようなケースに適合するのがBlameという機能です。

Blameとは

Blame自体はGitそのものに備わっているコマンドであり、特定のコード行についていつ誰が変更を入れたのか確認できるものです。リファレンスは英語になりますがこちらになります。

Blame(非難する、〜のせいにする)という言葉の本来の意味に近く、特定の変更が誰の責任に帰するものなのかを突き止める目的のコマンドです。

GitHub上で見るBlame

BlameはGitのコマンドですので、本来コマンドラインで使うものですが、GitHubはBlame用にUIを用意し、誰でも簡単にBlameの機能が使えるようにしています。

Atomプロジェクトを例にとってどんなUIなのか、何ができるのか見てみましょう。ここではcontext-menu-manager.coffeeというファイルを見てみます。

上記画面の右肩に"Blame"というボタンが見えます。このボタンを押すと、ビジュアルに確認することが可能です。押してみましょう。すると次のような画面になります。

このように、コード行毎にいつ誰が変更したのか、コミットメッセージと該当するSHAへのリンク付きで表示されます。Blameはさらにここから、特定のSHAに飛び、さらにそのSHAが生まれた原因まで遡ることも可能にします。

ここでは例として、6行目の{remote} = require 'electron'を見てみましょう。コミットメッセージの"Use the new style of `remote`"というところがリンクになっていますのでクリックします。すると次のように、該当コミットの変更内容を確認できます。

複数ある変更のうち、真ん中あたりに該当のcontext-menu-manager.coffeeの変更が次のように見つかると思います。この変更をみると、当時どのような変更が追加されたのかが分かります。

さらに、このコミットにはPull Requestへのリンクも貼られています。先ほどのコミットメッセージの下部、ブランチ名の右に括弧つきでリンクがあります。

このリンクから、次のPull Requestまでたどり着くことができ、この変更が行われた当時の議論状況、テスト結果、デプロイ結果等々、あらゆる付帯情報を確認できます。このようにPull Requestベースで開発することのメリットは、Blameによってコードから原因追求をする際にも有効なのです。

このPull Requestを順に見ていくと、次のコミットログに突き当たります。このコミットこそが先ほどの変更を起こしたものになります。

SHAの左にある緑のチェックマークをクリックすると、当時のこのコミットに対するテスト結果を確認することもできます。次のようになります。

また、このPull Requestが最終的に誰によっていつマージされたのか、そしてマージされたときの最終的なテスト結果はどうだったのか、も次のように確認できます。

このような情報がBlameを使うことでコード行から遡って追求できます。Blameの威力がよく分かるかと思います。また同時にPull Requestをベースに開発や議論、CIを行っているとどれほど恩恵を得られるか、についてもよくわかっていただけたかと思います。

さらに、このPull Requestに対してRevertをすることで、このコード行にまつわる変更すべてを安全に巻き戻すことすら可能です(AtomのこのPull Requestに関しては皆さんには書き込み権限がないため、Revertボタンは見えていません)。

最近追加された機能

2017年1月、Blameにはさらに新しい機能が追加されました。これはその変更が加わるまえのリビジョンのファイルに遡ることができる機能です。

UI上は次のボタンを押すことで利用できます。

この機能を使うことで、特定ファイルの特定行の変更を過去に遡っていって原因追求することが可能になります。この機能は2017年2月現在GitHub.comのみで利用可能で、GitHub Enterpriseにおいてはv2.10以降(現在はv2.8)に利用可能になる予定です。

まとめ

Pull RequestをベースとしたGitHub Flowで開発をすることで、Revertも簡単にできること、またBlameを使って特定のコード行からPull Requestにさかのぼって原因追求することが可能なこと、などを見てきました。

GitHubの持つ強力な機能がよくご理解いただけたかと思います。ぜひ日々の開発にGitHub Flowを取り入れて、さらなる開発効率化、品質向上を目指しましょう!

さて次回はマージのオプションについてお話しします。 GitHubでは最近、従来のMerge Commitを介したマージ(--no-ffマージ)に加えて、Squash mergeとRebase mergeをオプションとして加えました。 それぞれのマージオプション毎の違い、ユースケースなどを紹介します。

ソフトウェア開発者。大学卒業後、ITコンサルタントとしてキャリアをスタート。その後コンサルタントからプログラマーに転身し、パッケージソフトウェア開発、Webサービス開発を経て、2015年現在GitHubにてソリューションエンジニアとして働いている。著書に『チーム開発実践入門』(2014年 技術評論社)がある。

連載バックナンバー

Think ITメルマガ会員登録受付中

Think ITでは、技術情報が詰まったメールマガジン「Think IT Weekly」の配信サービスを提供しています。メルマガ会員登録を済ませれば、メルマガだけでなく、さまざまな限定特典を入手できるようになります。

Think ITメルマガ会員のサービス内容を見る

他にもこの記事が読まれています