テストファーストなGitワークフローについて

Gitのワークフローに関する話題が、また盛り上がっているようなので、僕が好んで使っているワークフローについて書きます。

対象としているソフトウェアは、GitHubGitHub Enterprise等を使って開発されている、リリースブランチを切らずにmasterにリリースタグを打っていくだけで十分な程度の、ウェブサービス(の部品)やオープンソースプロジェクトです。

まず、以下の2点を原則として考えています。

  • origin masterを壊さない
    • origin masterの(1st parentをたどるツリー)にテストを通らないcommitを入れないよう努めます
  • 変更の主題を常に明確にする

前者の理由は、masterをいつでもリリース可能な品質に保つためと(←12:44追記)git bisectするときに困らないようにするため。そして、これらの原則から、以下のようなワークフローで作業することが多いです。

  1. 変更を施す際には、まずトピックブランチを切る
  2. トピックブランチの最初のコミットは、「未解決の課題」を表すテスト(つまり失敗するテスト)
  3. 次に、この「課題」を解決する(1つ以上の)コミットを実装する*1
  4. その過程で、問題が見つかれば、さらにテストのcommitと課題解決のcommitを繰り返す
  5. 最後に、トピックブランチで作業している間にmasterが大きく進んでしまった場合は、トピックブランチにmasterをmergeして、conflictを解決する*2
  6. トピックブランチをmasterにmerge --no-ff*3し、テストが通ることを確認してpush(テストが通らなかった場合は5からやり直し)

なぜ、トピックブランチの最初のcommitを、「failするテスト」にするのか。それは、漫然とコードを書いてしまわないようにするためです。まず、ゴールを明確化し(必要ならチームメンバーで共有した上で)、そのために最適な実装を行うということを繰り返すためにも、この方法が良いのではないかと考えています。


参照:
誰得UNIX: git-flowでもgithub flowでもない、Git本家推奨のワークフロー
いまさら聞けない、成功するブランチモデルとgit-flowの基礎知識 (1/2):Gitブランチを使いこなすgit-flow/GitHub Flow入門(1) - @IT

PS. 単純な変更については、直接masterにコミットしても良いと思います。

PS2. BDD(ビヘイビア駆動開発)をGitのワークフローとして最小限の形で定式化するとこうなるという話として理解することも可能かと思います。

*1:たとえば、リファクタを行い、次に課題を解決するコードを実装するような場合は、コミットを分けるということ

*2:rebaseではなくmergeを使うのは、トピックブランチで行ってきた各commitの文脈を破壊しないため。rebase -iでコミットをひとつひとつ確認/修正しながらrebaseしてもいいですが、そこまでしてcommit treeの綺麗さを保つ必要には迫られていません

*3:このワークフローの場合、fast-forward mergeは第一原則に反する結果を産むので禁止