2014年12月17日
Git活用法 ー コードはいつも1行ごとにドキュメント化されている
本記事は、原著者の許諾のもとに翻訳・掲載しており ます。
コードには1行ごとに隠しドキュメントがあります。
次のコードスニペットの4行目を書いた人は、何か理由があってDOMノードの clientLeft
プロパティにアクセスしたのでしょうが、結果的に何もしていません。これはかなり不可解です。なぜこうしたのか、あなたは説明できますか? 今後、この呼び出しを変更したり削除したりしても安全でしょうか?
// ...
if (duration > 0) this.bind(endEvent, wrappedCallback)
this.get(0).clientLeft
this.css(cssValues)
私ではなく他の人があなたにこのコードを見せたとして、誰がこの行を記述したのか、どんな理由があったのか、このままの状態にしなければいけないのか、あなたはおそらく説明できないでしょう。ただし、プロジェクトを進めているときは大抵の場合、バージョン管理システムを使ってその履歴にアクセスできます。
プロジェクトの履歴は最も有用なドキュメントです。
この行について説明している次のコミットメッセージを読むと、謎が解けます。
$ git show $(git blame example.js -L 4,4 | awk '{print $1}')
DOMに追加しただけの要素についてanimate()を修正
DOMに追加しただけの要素の場合、CSSのtransitionはWebkitとMozillaのどちらでも有効になりません。この問題の対処方法として、以前はCSSのプロパティをsetTimeoutで設定していました(272513bを参照)。この方法でWebkitでの問題は解決しましたが、最新版のFirefoxでは解決しませんでした。Mozillaでは少なくとも15msのタイムアウトが必要のようですが、この値もさまざまのようです。
両方のエンジンにより適した解決策は、“layout”をトリガーすることです。この処理は要素からclientLeftを読み込むと実行されます。layoutをトリガーするプロパティやメソッドは他にもあります。 gent.ilcore.com/2011/03/how-not-to-trigger-layout-in-webkit を参照してください。
今にしてみると、この行(もっと具体的に言うと、この行について説明した変更)は、なぜこの行が必要だったのか、なぜ以前の方法(コミットSHAで参照する方法)は機能しなかったのか、どのブラウザが影響を受けているのか、そして詳細のためのリンクといった情報が だらだらとドキュメント化 されています。
そして実は、 この不可解な行を書いたのは私です 。もっと良いコードを書く方法はありました。例えば、 triggerLayout()
のような 目的が明らかな名前 の関数に 重要なプロパティアクセスをカプセル化 することもできましたし、せめて コードのコメントを追加 して、この行はアニメーションを開始するための行だと簡単に説明することくらいはできました。どういうわけか、私はその日、このコードを書くのに失敗したようです。 コードを書いても、必ずしも完璧とは限りません。
たとえこのコードがもっと理解しやすいものであったとしても、あるいはコードのコメント行が記述されていたとしても、プロジェクトの履歴の方がずっと充実した情報を提供することができます。
- このコードを書いたのは誰か
- このコードが追加されたのはいつか
- 付随するテストはどれだったか(該当するテストがある場合)
- 完全なコミットメッセージはストーリーになります(一方、コードのコメントは簡潔に記述する必要があります)
もちろん、コードの品質はとても重要です。しかし、コーディングをさらにもっと改善することを考えるなら、より良いコミットメッセージを書くことを目指すべきです。これはあなただけが目指すのではなく、あなたのチーム全体そして担当者全員が目指す必要があります。 ソフトウェアのストーリーというのは、その最新のチェックアウトと同じくらい重要です。
プロジェクトの履歴を効率的に調べる
git blame
コマンドラインから git blame
を使用する方法は上ですでに説明しました。Gitのローカルリポジトリにアクセスできない場合は、 GitHub上で任意のファイル の“blame”ビューを開くこともできます。
ファイルの履歴を効率良く調べるには、Vimと Fugitive を使用します。
- バッファで
:Gblame
を使用して、blameビューを開きます。 - さらに掘り下げる必要がある場合は、blameペインの行の上で Shift-P を押して、そのコミットの親でもう一度blameを実行します。
- o を押して、blameペインで現在選択されているコミットが表示されている分割ウィンドウを開きます。
- コミット分割ウィンドウで
:Gbrowse
を使用して、GitHub Webインターフェースでコミットを開きます。 - gq を押してblameペインを閉じて、メインバッファに戻ります。
詳細については、 :help Gblame
を参照してください。
コミットが発生したプルリクエストを探す
git blameを使えば、変更内容を説明したコミットSHAを取得できるかもしれません。ただしコミットメッセージは、変更の根本的な理由を説明できるだけの情報やコンテキストを必ずしも伝えるわけではありません。しかし、チームがプロジェクトで GitHub Flow を実践している場合、プルリクエストのやり取りの中でコンテキストが見つかる場合があります。
$ git log --merges --ancestry-path --oneline <SHA>..origin | tail
...
bc4712d Merge pull request #42 from sticky-sidebar
3f883f0 Merge branch 'master' into sticky-sidebar
このように、pull request #42による変更だということがコミットSHA 1つだけで分かりました。
git pickaxe(つるはし)
もうなくなっているもの(例えば、もはやどこからも呼び出されない関数呼び出しなど)を探したい場合もあるでしょう。特定のキーワードが追加されたり削除されたりしたコミットを探す最も良い方法は、 git log
に‘pickaxe(つるはし)’引数を使うことです。
$ git log -S<string>
この方法で、特定の関数の呼び出しを削除したコミットや、特定のCSSのクラス名を追加したコミットを発掘することができます。
git churn
プロジェクトの履歴を利用すると、コミットを個別に確認できるだけでなく、 変更のセットを全体的に分析できる ので、有益な見解が得られます。例えば、 git-churn はシンプルですが、 git log
をラップして、どのファイルが一番変更されているかについてステータスをまとめる便利なスクリプトです。次のコマンドは、過去6か月間にどのアプリ開発が重点的に行われたかを確認できます。
$ git churn --since='6 months ago' app/ | tail
ちなみに、このような分析はプロジェクト内の潜在的な技術上の問題を浮き彫りにすることもあります。特定のファイルがあまりにも頻繁に変更されているのは、ほとんどの場合、危険信号です。そのファイルのコードは頻繁にバグ修正が必要になっているか、もしくはそのファイルで行っている処理が大きすぎるため、もっと小さなユニットに分割する必要がある可能性があります。
同様の方法で履歴分析を行えば、コードベースの特定部分を最近開発していた担当者を確認することができます。例えば、アプリケーションのAPI部分に最も関与した人を確認するには、以下のコマンドを実行します。
$ git log --format='%an' --since='6 months ago' app/controllers/api/ | \
sort | uniq -c | sort -rn | head
109 Edmond Dantès
13 Jonathan Livingston
7 Ebanezer Scrooge
履歴の正しい作り方
今あなたが作成しているものはすべて、プロジェクトの履歴に登録されて、永遠に残されるということを頭に入れておいてください。一緒に作業している他の人たちにとって親切になるように(たとえ1人のプロジェクトであっても、3ヶ月後のあなた自身も作業者です)、コミットするときは以下の基本ルールに従います。
- コミットメッセージは常に、あなたの隣で作業していて状況が理解できていない仲間に 変更内容を説明する ように書きます。 Thoughtbotのより良いコミットメッセージを作成するためのヒント によれば、以下のとおりです。
次の質問に答えてください。
- この変更はなぜ必要なのですか?
- この問題にどうやって対処しますか?
- この変更に伴う、思わぬ影響は何ですか?
- リンク(ディスカッションへの)を記載するようにしてください。
-
関係ない変更を1つのコミットに含めない。 他の変更を行ったファイルで、入力ミスが見つかったりコードのリファクタリングを少ししたりするかもしれません。でも直接関係が無い限り、メインの変更と一緒にそのような変更をするのは避けてください。
-
プッシュの前に必ず履歴をクリーンアップする。 コミットがまだ共有されていない場合は、安全に rebase を使うことができます。以下の履歴はFaradayプロジェクトで永遠に維持されていたかもしれませんが、2件だけのコミットに圧縮し、それらのメッセージを編集して、最初の段階でスクリプトのセットアップに問題があったことを隠しました。
- 関係ない変更を避けるための方法: 行ベースのコーディングスタイルにこだわる と、隣接する行を変更しないで、リストの値を追加、編集、削除することができます。以下に例を示します。
var one = "foo"
, two = "bar"
, three = "baz" // Comma-first style allows us to add or remove a
// new variable without touching other lines
# Ruby:
result = make_http_request(
:method => 'POST',
:url => api_url,
:body => '...', // Ruby allows us to leave a trailing comma, making it
) // possible to add/remove params while not touching others
どうしてこのコーディングスタイルにする必要があるのでしょう? git blame
を使おうとする人のことをいつも考えるようにしてください。このJavaScriptの例で、コミット済みの値 "baz"
を追加したのがあなただったとしたら、誰かが "bar"
を追加した行でblameを使ったときに自分の名前が表示されるのは嫌ですよね。この2つの変数に関連性はないかもしれないからです。
ボーナススクリプト
ここまで読んでもらったお礼に、おまけのスクリプトをお見せしましょう。私はこれに git-overwritten という名前を付けて、任意のブランチで変更または削除した行の元の担当者についてblameの情報を表示しています。
$ git overwritten feature origin/master
28 2014-02-04 1fb2633 Mislav Marohnić: Add Makefile for building and testing
1 2014-01-13 b2d896a Jingwen Owen Ou: Add -t to mktemp in script/make
17 2014-01-07 385ccee Jingwen Owen Ou: Add script/make for homebrew build
これは GitHub Flow でプルリクエストを開くときに便利です。同僚に自分のプルリクエストを確認してほしくても、誰にメッセージを送ればいいか分からない場合があります。 git-overwritten
を使えば、あなたが変更した行を書いた元の人たちの名前が分かるので、プルリクエストを開くときに相手先を把握できます。
株式会社リクルート プロダクト統括本部 プロダクト開発統括室 グループマネジャー 株式会社ニジボックス デベロップメント室 室長 Node.js 日本ユーザーグループ代表
- Twitter: @yosuke_furukawa
- Github: yosuke-furukawa