Bazaarで歴史改変
Bazaarの場合、「メインライン」という概念があって、そこさえちゃんとしていればよしという感覚なので、フィーチャブランチなどの履歴を改変して整理するっていうのは、あまりする必要がありません。
とはいえ、それでも履歴の改変がしたい場合もあります。例えば、
- はじめての相手にマージリクエストを出すときはかっこつけたいし、しょうもないミスは消しておきたい
- 思った以上に履歴が長くなってしまったので、もう少し分割して個々にレビューを受けたい
みたいな。という訳で、今日は歴史改変ネタで行きたいと思います。
標準機能でがんばってみる
実は、Bazaarの標準機能だと、あまり履歴の改変に使えるコマンドがありません。
基本的には、以下の2つの組み合わせになります。
- uncommitやpullコマンドでコミットを取り消す
- mergeコマンドで、必要なリビジョンをチェリーピッキングする
uncommitで、最新のコミットを取り消す
# 最新のコミットを取り消します。
$ bzr uncommit
# 最新と、そのひとつ前のコミットを取り消します。uncommitでは「どれを消すか」ではなく「どこまで戻すか」を -r で指定します。
$ bzr uncommit -r -3
pullで、最新のコミットを取り消す
# 最新のコミットを取り消します。※uncommitの場合、作業ツリー内のファイルはコマンド実行前のままですが、pullの場合は作業ツリー内のファイルも昔の状態に戻ります。つまり、[uncommit + revert == pull]ということになります。
$ bzr pull . -r -2 --overwrite
# 最新と、そのひとつ前のコミットを取り消します。
$ bzr pull . -r -3 --overwrite
mergeでチェリーピッキング
# リビジョン5の変更内容を、ブランチcolo:fooから取り込みます。※ 「リビジョン5での変更」ということで、"-r 4..5" のかわりに "-c 5" と指定することもできます。
$ bzr merge colo:foo -r 4..5
標準機能でがんばってみる(例題)
例えばこんな履歴があるとして、
444: IWATA 2011-12-07 Reduce codes triggered b ... 443: IWATA 2011-12-07 Fix : WtCacheEntry may b ... 442: IWATA 2011-12-07 [merge] Fix small mistak ... 441: IWATA 2011-12-07 Now, we don't have to re ... 440: IWATA 2011-12-07 Fix problems about 'show ... 439: IWATA 2011-12-07 Add some tests 438: IWATA 2011-12-06 Rewrite WtCacheEntry -- ...
その1:r442〜r444をひとつにまとめる
$ bzr uncommit -r 441これは簡単ですね。
$ bzr commit -m "新しいメッセージ"
その2:r439を抹消する
コピーを作って、元のブランチはr438まで戻すこれはかなり面倒です。ていうか手作業でするようなことじゃないです。
$ bzr branch . ../copy
$ bzr pull . -r 438 --overwrite
copyから、r440〜r444をひとつずつ移植
$ bzr merge ../copy -c 440
$ bzr commit -m "Fix problems about 'show ..."
$ bzr merge ../copy -c 441
$ bzr commit -m "Now, we don't have to re ..."
・・・(以下略)・・・
Gitならrebase -iがあるし、Mercurialならtransplantやgraftがあるのに・・・。
と長らく思ってたんですが、
rewrite_interactiveを使ってみる
最近BazaarのMLで、「Gitのrebase -iと同じことするプラグインを実装したぜ」っていう方がいました。
→ OrderedDict data structure in bzrlib?
すばらしいですね。ソースを公開されてますので、これを試してみましょう。
#プラグインフォルダにrewrite_interactiveという名前で配置しますさっきの例題をやってみます。
$ bzr branch http://dl.dropbox.com/u/1185733/src/bzr-rewrite_interactive/ rewrite_interactive
444: IWATA 2011-12-07 Reduce codes triggered b ... 443: IWATA 2011-12-07 Fix : WtCacheEntry may b ... 442: IWATA 2011-12-07 [merge] Fix small mistak ... 441: IWATA 2011-12-07 Now, we don't have to re ... 440: IWATA 2011-12-07 Fix problems about 'show ... 439: IWATA 2011-12-07 Add some tests 438: IWATA 2011-12-06 Rewrite WtCacheEntry -- ...
その2:r439を抹消する
コピーを作って、元のブランチはr438まで戻すそうすると、エディタでこんな感じのテキストが表示されます。
$ bzr branch . ../copy
$ bzr pull . -r 438 --overwrite
rewriteコマンドでcopyからr440〜444を移植
$ bzr rewrite ../copy -r 438..444
pick 439 Add some tests pick 440 Fix problems about 'show ... pick 441 Now, we don't have to re ... pick 442 Fix small mistakes pick 443 Fix : WtCacheEntry may b ... pick 444 Reduce codes triggered b ... -------------- This line and the following will be ignored -------------- pick = use commit edit = use commit, but stop for amending squash = use commit, but meld into previous commit
このファイルの1行目を削除して、保存してエディタをクローズします。コンフリクトが発生しなければ、それで移植完了です。
コンフリクトが発生した場合は、それを解消してから、rewrite-continueコマンドを叩きます。
$ bzr rewrite-continueずいぶん簡単になりました。
その3:r439〜r441をまとめる
その2と同じようにコマンドを実行して、表示されたテキストの2行目、3行目を pick → squash に書き換えます。
2行目|squash 440 Fix problems about 'show ... 3行目|squash 441 Now, we don't have to re ...
この操作だとコンフリクトは起きないので気が楽ですね。
標準のプラグインにもrewriteってのがあって(そっちにはrewriteコマンドではなくrebaseコマンドが入っている)紛らわしいんですが、最終的にはそっちに統合されることになるかもしれません。
ともあれ、作者の方に感謝です。
という訳で、Bazaarによる履歴改変方法の紹介でした。