未来のいつか/hyoshiokの日記

hyoshiokの日々思うことをあれやこれや

Rebase Early, Rebase Often

昨日会社のブログ(git入門/ユメのチカラ)gitの話をしたが、その続き。(資料はこちら)

開発は基本的にはリモートのレポジトリをローカルにコピーして、そのコピー(ブランチ)に対して変更を行い、ローカルにコミットを繰り返す。

ローカルなブランチに対するコミットがずんずん増えていくわけであるが、それと並行してリモート(マスタ)もどんどん変更されていく。ローカルな変更とオリジナルな変更をいつの時点かで同期をとらないといけない。ローカルな変更はいわば、オリジナル対する機能追加であったり機能変更であったり、あるいは単純なバグフィックスであったりするわけであるが、開発期間が長ければ長いほど、ローカルな開発期間中にオリジナルが相当量変更されることになる。

最終的にオリジナルにマージするには、並行開発期間中のローカルな変更を矛盾なくオリジナルに統合する必要がある。

ローカルなブランチ1をZの時点で作りそれに対していろいろ変更(コミット)をして最終的にはCの時点でマージをしたとする。

--Z--o--X--...--o--A--C--...--D (オリジナル)
  \                  /
   1--o--Y--...--o--B (ローカル)

Xの時点で関数Fooに対し意味的な変更を加えたとして、一方でローカルなブランチ作業中Yの時点で関数Fooを呼ぶような変更を加えたとする。ここでローカルなブランチは昔の意味で関数Fooを呼ぶように作った。Zの時点で分岐しているので、Zから分岐したブランチは当然古いFooの動作なので、Yを作った時点では正しいし、このブランチでの意味的な不整合はない。

さてCの時点でマージをする。ローカルからはgit pushなどというコマンドで行う。字面上の矛盾がなければコンフリクトは発生しないのでマージそのものは成功する。Fooの変更に関して言うとYでは古い意味論で関数Fooを利用しているので論理的なバグを埋め込んでしまったことになるが機械的には発見できない。

このバグは未来のいつかDの時点で発見されたとする。git bisectは2分検索で最初に不具合を導入したコミットを自動的に発見してくれる。Dで発見した不具合はZの時点ではなかったので

$ git bisect start
$ git bisect good Z
$ git bisect bad D

などとしてテストを繰り返せば、最小手順でCが最初に不具合を導入したコミットだということがわかる。これはこれですごいのだが問題はオリジナルのブランチだけを調べても、ローカルなブランチだけを調べても問題の原因は発見できない。つまり関数Fooに対する変更が絡み合ってこの問題を生じさせているので、それについての深い洞察力がないとこの問題の原因を発見することは難しい。

そこでrebaseというコマンドを使う。

--Z--o--X--...--o--A                     --C--...--D (オリジナル)
                    \                   /
                     1--o--Y*--...--o--B* (ローカル)

Aの時点でローカルな変更を$git rebaseする。そうするとローカルな変更はあたかも、Aを起点とした変更のようにマージしてくれる。字面上のコンフリクト等が発生した場合それを手で修正し、オリジナルのブランチへマージ($ git push)する。そうすると、二本の線をマージしたというよりも一本の長い線になる。

Y*での変更についても最終的にはDの時点で問題が発見されるのであるが(これはさっきと一緒)、今回はZからDまでのブランチが一本なので$ git bisectで効率的(機械的)にY*が問題を発生させた最初のコミットであると発見できる。

このように早めににrebaseを行い、こまめにrebaseをすれば、バグを素早く発見できるし、バグの原因究明も容易になる。

Rebase Early, Rebase Often

ハッカーのベストプラクティスであり、それを支援する仕組みを持っているgitというツールというのはなかなか奥が深いのである。

昨日のブログSHA1はグローバルでユニーク性が保証されていると記したが厳密に言えば低い確率ではあるがコリジョンは発生する可能性がある。

git bisectに関しては下記を参考にしてほしい。カーネルにおけるリグレッションの特定/ユメのチカラ http://blog.miraclelinux.com/yume/2007/10/post_d748.html