1年目から身につけたい! チーム開発 6つの心得

第5章 情報価値の高いコミットをしよう―コードだけではわからない意図を伝える技術

この記事を読むのに必要な時間:およそ 9.5 分

ついでの修正をまとめてコミットしない

規模の大きなコミットの別パターンとしてよくあるのが,ついでコミットです。

リスト1は,引数が何も指定されていなければ「Hello,world!」と出力してすぐに終了し,そうでなければ後続の処理をするという実装です。これに対して,処理の最後にログを出力するようにしたのがリスト2です。その際,if文の条件の丸括弧と波括弧の間にスペースがないことに気づいて修正しており,また,もともと呼び出していた初期化のための関数の名前を間違えていたことにも気づいて,こちらも併せて修正しています。

リスト1 変更前のコード

function startup() {
  if (arguments.length > 0){ // ここだけ波括弧の前にスペースがない
    console.log("Hello, world!");
    return;
  }
  initalize(); // 関数名が間違っている
  process();
}

リスト2 変更後のコード

function startup() {
  if (arguments.length > 0) { // 波括弧の前にスペースを挿入した
    console.log("Hello, world!");
    return;
  }
  initialize(); // 誤記を訂正した
  process();
  outputLog(); // ログ出力の関数を呼ぶようにした
}
...

function outputLog() {
  // ログ出力の処理をここで実装した
}

この状態で変更をコミットすると,3つの変更がすべて含まれたコミットになってしまいます。これが「ついでコミット」です。

事情を知らない人がこのコミットを見たら,⁠波括弧の前のスペースもログ出力のために必要だったのか?」と思われてしまいかねません。また,⁠ログ出力をやめよう」と思ってこのコミットを取り消すと,誤記の修正まで巻き添えを食って取り消されてしまいます。そのことに気づかないまま作業を続けてしまうと,直したはずだった誤記のせいで再び問題が発生して,原因究明に余計な手間がかかってしまう,ということも起こり得ます。

このようにいくつもの弊害があるので,⁠ついでコミット」は原則として避けたほうがよいです。

注5)
実際,有名なOSSプロジェクトでもそのようなコミットはよく見られます。ミスをするのは世界中どこにでもありふれたことなのです。
ミスやスタイルを修正するコミット

ついでコミットをしないためには,コーディングスタイルを直したら「Fix coding style」とコミット,誤記を見つけたら「Fix typo」とコミットするという風に,どんな細かな修正でもその都度コミットするようにすることが大切です。

「恥ずかしいミスだからこっそり直したい」と考える人もいるかもしれません。しかし,ミスをごまかしたいという動機で1トピックごとに1コミットというルールを曲げてはいけません。恥ずかしがらずに堂々と,個別の変更としてコミットしましょう注5)⁠

また,コーディングスタイルを修正するコミットのときは,インデント幅の修正であればインデント幅だけ,改行位置の変更であれば改行位置だけという風に,コーディングスタイルの個々の項目ごとにコミットすることも大切です。そうすることで,コミットメッセージもより簡潔なものになります。たとえば,このような場面でよく使われるコミットメッセージには次のようなものがあります。

  • Align:連続する代入文の右辺などの縦の位置をそろえたとき
  • Indent:インデント幅を増やしたとき
  • Unindent:インデント幅を減らしたとき
  • Fix indent:一部の行だけのインデント幅が崩れたものをそろえたとき
  • Tabify:ソフトタブをハードタブで置き換えたとき
  • Untabify:ハードタブをソフトタブで置き換えたとき

改行位置や空白の空けかたなどの修正については,簡潔に言い表せる表現がないため「Fix coding style」というコミットメッセージが使われることもあります。

--patchオプションを使って分割しよう

ただ,気をつけていても,⁠つい熱中して,一気にやってしまった」⁠ついつい,一緒に修正してしまった」ということはやはり発生しがちです。そんなときでも,変更を個別に分割してコミットできます注6)⁠

具体例として,リスト1のコードをリスト2のように変更するコミットの場合を考えましょう。

git addコマンドまたはgit commitコマンドに--patchオプション(短縮形は-pを指定すると,あとからでもそれぞれの変更を分割してコミットできます。具体的には,Unified Diff注7)⁠ の一つ一つのhunk注8ごとにコミットするかどうかをインタラクティブに選択できます。図4は,リスト2の変更を行った状態でgit add -pしたときの様子です。

図4 ブロック単位のdi

diff --git a/sample.js b/sample.js
index f649872..d64a4bf 100644
--- a/sample.js
+++ b/sample.js
@@ -2,7 +2,7 @@
  function startup() {
-  if (arguments.length > 0){
+  if (arguments.length > 0) {
     console.log("Hello, world!");
     return;
  }
-  initalize();
+  initialize();
  process();
+  outputLog();
  }
Stage this hunk [y,n,q,a,d,/,j,J,g,e,?]?

図4には,このブロックの変更点として「コーディングスタイルの訂正」⁠誤記の訂正」⁠関数呼び出しの追加の変更」の3つすべてが表示されていることがわかります。このとき取れる操作にはいくつかの選択肢がありますが注9)⁠代表的なものは次のとおりです。

  • y(yes,はい)
  • n(no,いいえ)
  • s(split,分割)
  • e(edit,編集)

yを選択するとそのブロック全体がコミットに含められます。逆に,nを選択するとそのブロック全体がコミットから除外されます。多くの場合は,この2つを使ってブロック単位で変更をコミットに含めるかどうかを決めることになります。

sを選択すると,ブロック内の複数の変更個所がさらに図5のように細かい単位に分割されて,再びそのブロックへの操作を尋ねられます。今回のように1つのブロックの中に複数の変更が含まれてしまった場合は,この方法でブロックを小さくしてから,個別にyまたはnを選択することで必要な個所だけをコミットできます。

図5 小さなブロックに分割されたあとのdi

diff --git a/sample.js b/sample.js
index f649872..d64a4bf 100644
--- a/sample.js
+++ b/sample.js
@@ -2,7 +2,7 @@
  function startup() {
-  if (arguments.length > 0){
+  if (arguments.length > 0) {
     console.log("Hello, world!");
     return;
  }
Stage this hunk [y,n,q,a,d,/,j,J,g,e,?]?

また,eを選択すると,テキストエディタ(Vimなど)を使って,コミットに含める変更を行単位で編集できます。この場合には,diffの行頭の-を半角スペースに置き換えて行の削除を取り消したり,行頭が+である行を削除して行の追加を取り消したりできます。sではどうしても思ったとおりの単位に変更個所が分割されない場合の最終手段と言えます。

これらの選択肢を使い分ければ,複数の変更が入り交じったコミットを個別のコミットに分けられます。変更をコミットするときにgit add -pgitcommit -pを使うクセを付けておくと,うっかり複数の変更を一度にコミットしてしまうことを防げます。

注6)
バージョン管理システムによってはできません。
注7)
Diffの形式の一種です。
注8)
ひとまとまりの変更のことです。
注9)
最後の行のStage this hunkのあとに書かれている文字がそれぞれの操作を表しており,文字を入力することで該当する操作を選択します。

著者プロフィール

足永拓郎(あしえたくろう)

株式会社クリアコード所属。デスクトップや組み込みソフトウェアの自由を求めて活動中。


結城洋志(ゆうきひろし)

株式会社クリアコード所属。Firefox黎明期からアドオン開発を手がけ,業務上もMozilla製品の技術サポートを担当。代表作は「ツリー型タブ」「テキストリンクなど。また,日経Linux誌にて「シス管系女子」を連載中。


林健太郎(はやしけんたろう)

株式会社クリアコード。趣味であれこれと,Sylpheedのプラグインを開発したり,Debianのパッケージをいくつかメンテナンスしている。

Twitter:@kenhys

コメント

コメントの記入