本当は怖いローカルコミット

前にShibuya.TracとかOSCでBazaarについて話したときには、

Bazaarだったら、SVNみたいに集中型で運用しながら、必要に応じてローカルでコミットすることもできるよ

みたいなことを言ったんですが、最近はちょっと、ローカルコミットってのは挙動にクセがあるから混乱の元だし、使わない方が良さそうだという結論に(僕の中では)なってます。ローカルでコミットしたい場合はちゃんとブランチ切ろうねってことで。

ということで、お詫びもかねて、ローカルコミットを使うとどうなるかというのをちょっとご紹介します。

Pending Merge

まず、前提となる用語について。
Bazaarの作業コピーには、"Pending Merge"という状態があります。これは、ここの「ステップ2:分散型の導入」のあたりにある、「mergeを実行したけれど、まだcommitはしていないよ」という状態のことですね。
http://standing-shoebill.appspot.com/bzr-startup-guide/scmbc-2.html#id9

ここからcommitすれば、マージした内容が確定しますし、revertすればmergeを実行する前の状態に戻ります。

ローカルコミット

では、実際にローカルコミットを実行するとどうなるかやってみます。
まずはブランチを作成。

$ bzr init test
Created a standalone tree (format: 2a)
$ cd test
$ echo foo>foo
$ bzr add
adding foo
$ bzr commit -m "最初のコミット"
added foo
Committed revision 1.
それからそのブランチをチェックアウト
$ bzr checkout . ../checkout
$ cd ../checkout
チェックアウトした作業コピー上で、ローカルコミットしてみます。
$ echo fooo>>foo
$ bzr commit -m "ローカルコミットしてみる" --local
modified foo
Committed revision 2.
これで、作業コピー上でローカルコミットできました。作業コピーのログは、今こうなってます。
$ bzr log --line
2: IWATA Hidetaka 2011-12-02 ローカルコミットしてみる
1: IWATA Hidetaka 2011-12-02 最初のコミット
そのまま作業を続けます。次のコミットで、さっきのとまとめてチェックアウト元のブランチにも反映しましょう。
$ echo foooo>>foo
$ bzr commit -m "今度は普通にコミット"
これで、変更が反映されて、こんな感じのログになるはずです。そう思ってた時期が僕にもありました。
3: IWATA Hidetaka 2011-12-02 今度は普通にコミット
2: IWATA Hidetaka 2011-12-02 ローカルコミットしてみる
1: IWATA Hidetaka 2011-12-02 最初のコミット
残念!さやかちゃんでした。 そうはなりません。このコミットは失敗します。

bzr: ERROR: Bound branch BzrBranch7(...) is out of date with master branch BzrBranch7(...).
To commit to master branch, run update and then commit.
You can also pass --local to commit to continue working disconnected.
作業コピーが古いので、まずupdateしろよって言ってます。何で?
とにかく、言われたとおりupdateしてみましょう。
$ bzr update
All changes applied successfully.
Updated to revision 1 of branch ***
Your local commits will now show as pending merges with 'bzr status', and can be committed with 'bzr commit'.
何かごちゃごちゃ言ってます。ここでログを確認してみると、リビジョン2が消えちゃいました!
$ bzr log --line
1: IWATA Hidetaka 2011-12-02 最初のコミット
うそです。消えてません。qlogで履歴を見ると分かるんですが、

作業コピーがPending Mergeの状態になって、リビジョン2(だったもの)はコミット待ちの状態になっているんですね。

これで改めてcommitすると、ようやく変更がチェックアウト元に反映されます。

$ bzr commit -m "今度は普通にコミット"
Committed revision 2.
ログはこうなりました。

イヤなポイント

というわけで、ローカルコミットのイヤポイントをまとめます。

分かりづらい

そもそも、「まずは集中型で」っていう運用は、まだDVCSになれていない人たちに勧めたいパターンです。
Pending Mergeっていうのは本来ブランチ間のマージによってできる状態なので、それが理解できてるなら普通にDVCSとして使えますしね。

危険

Pending Merge状態になったところで、「良く分からないし一旦revertしてやり直そう」とかすると、コミットしたはずの
リビジョン2が本当に消えてしまいます。(データそのものが削除されるわけではないので復元することはできますが)
「え?ちゃんとコミットしたはずなのに、何で?」って感じです。

そもそもやりたいことと違う

別に、ローカルコミットって、分岐を作りたくてする訳じゃないですし。他の人の更新とバッティングして分岐ができるのは仕方ないけど、他に誰も更新してなくても分岐ができてしまうのってどうなのよと。


というわけで、チェックアウトのローカルコミットは初心者にはおすすめできないよ、って話でした。
初心者じゃない人にはお勧めかっていうと、そういう人は普通にブランチ切るだろうから、そもそもローカルコミット使わなそうですしね。

ちなみに、ローカルコミットした後、普通のコミットをするかわりに

$ bzr push ../test
ってすれば、リビジョン2をそのままチェックアウト元のブランチに反映することが出来ます。
分かんないよねそんなの。