2019.07.26
むぷん

リーダブルコミット

初投稿のむぷんと申します。よろしくお願いします。

私もエンジニアの端くれですので、初投稿記事はTech系にしてみました。
テーマは、バージョン管理システムの中でも特に広く使われ、
今や業界のデファクトスタンダードとなった、Gitに関する話です。

Gitの特徴といえば、まず第一にブランチです。
ソースコード変更の歴史を一本の世界線…もといブランチとすることで、
バージョンの切り替えやロールバックを手軽に行うことができる利点があります。

しかし今回の話題はブランチ云々ではなく、ブランチの構成要素、
すなわち変更履歴の最小単位であるコミットに関するポエム小話です。
コミットを少しでも読みやすく、保守性の高いものにするためのノウハウについて語ろうと思います。

読みやすいコミットとは

さて、ソフトウェア開発が自分一人で完結することはそれほど多くありません。
業務として開発を行っているのであれば尚のことです。

多人数での開発プロジェクトを円滑に進めるためにソースコードの可読性を上げることは
書籍やコミュニティ内で数々の議論がされています。

同様に、Gitのコミットについても他人が(自分も)理解しやすいように作ることが求められます。

コミット作成のルールは、概ねプロジェクトに依存するものです。
(例えば、コミットメッセージにGitHubのIssue番号を紐づけるなど)

しかし、どんなプロジェクトにおいても私が意識しているコミットの大原則は
atomic(原子的)であることです。

すなわち、
「変更範囲を最小限に、かつ意味のある単位でまとまっていること」
になります。

以下に詳しく説明していきます。

変更範囲を最小限に

Git初心者のうちは、巨大な差分をもつドッカンコミットを作ってしまいがちです。

ただ単にソースコードを定期バックアップしたいだけであれば事足りますが、
後々、コミットログから変更の意図やバグの原因を突き止めることが困難になります。

また、プルリクエストを受け取る側の視点からすると
数千行もの差分を突き付けられたら、きっと泡を吹いて倒れそうになること請け合いです。
(もちろん、外部のライブラリファイルがaddされた場合などは許容すべきです)

そこで、日頃からコミットを細かく区切る習慣を付けてみましょう。
例として、次のTypeScriptで書かれたサンプルコードをご覧ください。


こちらはCatクラスの定義および関数呼び出しを行っている例です。
Node.js環境下でコンパイル&実行してみると以下のような出力が得られます。


ここに以下のような変更を施し、コミットを作ったとします。


コミットメッセージから察するに、say()関数により出力される文字列を変更したという趣旨のコミットでしょう。
しかし差分をよく見てみると、「ハードコードされた文字列をメンバ変数として切り離した」という、
全く異なる意図の変更内容が含まれています。

それじゃあ、コミットメッセージにその旨を追記すれば解決!
…とするのも悪くありませんが、そもそもこの2つの変更点は意味が独立しています。
この場合は以下のように、コミットを2つに分割するべきです。


※実際は各コミットごとに「変更を施した理由や経緯」までメッセージに含めるのが理想的です。

このように、コミットを可能な限り小さくまとめることで、
1コミットあたりの情報をより明確にすることができます。
プルリクレビュアーのワーキングメモリを無駄に占有させずに済むでしょう。

ただし、無意味にコミットを小さくしすぎることも当然ながら御法度です。
次の例をご覧ください。

意味のある単位で


こちらはsay()関数をリネームしたという単純な変更です。
上の例では定義側と呼び出し側、各1行の変更についてコミットを作っていますが、
このままの状態で共有リポジトリにpushするのはNGです。

というのも、この2点の変更は不可分であり、セットにしなければ無意味な変更だからです。

※もしも定義側のみ関数名が変更された状態のコミットにチェックアウトした場合、
 当然、「そんな関数…ウチにはないよ」みたいなことを言われてまともに動きません。
 そのようなコミットを(作業用のブランチならともかく)masterブランチに取り込むことは好ましくありません。

したがって、この2行はまとめて1つのコミットとするべきです。

まとめ

以上、単純かつやや極端な例で説明した感がありますが、
これくらいの運用ポリシーは常に意識してよいと個人的には思います。

atomicな粒度のコミットは可読性に優れ、ソースコード変更経緯を追跡しやすくなります。
ですがそれだけではなく、Git運用上でのメリットも生じます。
例えば、

  • git log -S や git log --grepで探したいコミットログを検索しやすくなる
  • git revertで必要な範囲に絞ったロールバックができる
  • git bisectでデグレポイントを絞り込みやすくなる

などが考えられますね。

とはいえ、実際の現場で理想的なコミットを一発で作れることはそうそうなく、
後付けでコミットをちぎってはくっつけ、ちぎってはくっつけ…と、
スクラップ&ビルドを繰り返すことが多いです。

そのためのコミット操作に関するコマンドもいくつか扱える必要があるのですが、
(git add -pとかgit rebase -iとか)
そちらは次回以降に解説したいと思います。

【参考資料】
今回取り上げた内容がより本格的に解説されています。
Gitを常用されている方はご一読推奨です。

『きれいなcommit, pull requestを知りたい/作りたい方のためのgit勉強会』

以上です。

むぷん

一覧に戻る