Gitリポジトリにあるコードを追う / コードレビュー

21st Feb, 2010 (Updated on 23rd Feb, 2010) | development git

これまではcommitされたコードを、commit(push時)メールでなんとなく見ていたが、取りこぼしも多いし、忙しいと、つい見なくなってしまうので、なんかいい方法はないかなとここ数カ月くらいぼんやり考えていた。で、簡単なスクリプトでできそうと気づいたのでメモ。Githubに置いてあるようなオープンソースなコードとかも追いやすいんじゃないかなー。

ちなみに、このスクリプトを書く前に、コードレビューシステム的なのを導入しようかとGerritとか、Review Boardを少し試してはみた。でも、うちで使うにはちょっと大げさ過ぎるので、導入してもツールに踊らされる or 使わなくなる、という感じがしたので、とりあえずやめた。

いまいち気に入らない点としては、Gerritとかは完全にcommit単位でのレビューなんで、ちょっとしたパッチレベルならいいのだが、がりがり書いていく中ではちょっと現実的ではないかな、という感じがした。同じ機能追加/修正のために、同じ日に同一のファイルの同じような場所に複数回commitする、ということはよくあると思うが、そういう場合に1 commitずつ見る意味はあまりないかな、という感じ。それと、普段エディタで書いてるので、レビューもエディタでしたい、ってのもある。

追記: 指摘されたのだが、もちろんGitのsquash機能を使いこなせば、きれいな1 commitにまとめられることも多いだろう。でも、それでもやはり限界はあるし、綺麗なcommitセットとログを作ることに時間をかけるのも本末転倒な部分もあるかな、と。もちろん 1 commit は 1機能の追加/修正するという原則は基本的には徹底したいんだけど。それと、個人的には複数人が同じようなところ修正した場合も一個の差分として見た方がわかりやすい。

自分の中でやりたいことをまとめると、

  • 毎日レビューするという前提 > そこまで大量なコードを相手にするわけではない
  • リポジトリの(あるブランチ)中全部のコードを対象にしたい
  • 前回レビューしたところから現在までのソースの差分を見たい
  • 同一のファイルに対する複数回のcommitは一個の差分として表示
  • ただしcommitログはファイル毎に全部表示
  • レビューで問題や疑問があった場合には普段使っているBTS(Redmine)に投げる (ここはスクリプト化しない)
  • 毎日のことなので、上記のことをコマンド一発で簡単にやりたい

こんな感じのスクリプトを作った。ほとんどGitのコマンド並べただけだけど、短いので全文出しとこ。

#!/bin/sh

if [ ! -d ".git" ]; then
  echo "run in the git top directory."
  exit 1
fi

reviewed_repo="REPOSITORY_TO_STORE_REVIEWD_CODE"
reviewed_branch="BRANCH_FOR_REVIEWED_CODE"
reviewed_combined="${reviewed_repo}/${reviewed_branch}"

output_file="reviewing.patch"
rm -f ${output_file}
for file in `git diff --name-only ${reviewed_combined}`; do
  echo "==================================================================" >> ${output_file}
  git log ${reviewed_combined}.. -- $file >> ${output_file}
  echo "" >> ${output_file}
  git diff ${reviewed_combined}.. -- $file >> ${output_file}
  echo "" >> ${output_file}
done
if [ -f ${output_file} ]; then
  emacs -nw ${output_file}
  read -p "Approved?(y) " ans
  if [ "${ans}" == 'y' ]; then
    git push ${reviewed_repo} HEAD:${reviewed_branch}
  else
    echo 'ignored'
  fi
else
  echo "No unreviewed commit."
  exit 1
fi

処理の流れ: (1-2 をするようなGitコマンドがありそうだが、探せなかった)

  1. git diff --name-only で前回から変更があったファイルの一覧取得
  2. 変更があったファイル毎に、git logとdiffで自分が欲しい情報作成
  3. 作成した差分をエディタで開く
  4. エディタを終了したら、プロンプトを出して、reviewが終わったか聞く
  5. 終わった場合には、現在までのcommit情報をレビュー済branchにpush

「前回レビューしたところまで」という情報をGitにブランチとしておくことで、未読管理とかめんどくさいことを全部gitにお任せできるので楽。これを置くところはローカルでもリモートでもいいんだけど、今回は未読情報を一箇所に置いておきたのでリモートに置いている。

たったこれだけなんだけど、なかなか気に入っていて、一日の中で時間が空いたときに、reviewしたいリポジトリに行って、 rv (このスクリプトに付けてる名前) とタイプするだけで、前回レビュー時からの差分が、慣れている emacs に出てきて、レビューが開始できる。オリジナルのソースにジャンプしたい場合とかもemacsのdiffモードにお任せなので楽々。

前もってpullさえしてあれば、オフラインでも当然動くので(最後のpushは今はリモートのリポジトリにしてるのでそこだけは場合によってネットいるけど)、電車で移動中とかどこでもできるのもいい。

たいしたことではないけど、日常なことを簡単にできるようにするってのは効率化(=自分の本当にやりたいことの時間を増やす)ための一歩ですよね。

しばらくこれで行ってみて、きつくなったら、レビューシステムを再検討してみるつもり。

追記: はてブでわかりにくいという反応を頂いたので図を書いてみた。数字は上のリストの数字と対応してる。正確にはReviwed branchはローカルに持ってきてからlogとdiffをする感じ。

全体的に、なんかもっといいGit標準的なやり方がありそうで、ちょっと自信ないのだけど。