bzr-git で快適 github 生活

Bazaar のホスティングサイトとして、 Launchpad があります。これはプロジェクトをまたぐ Issue Tracker やオンラインの翻訳システムが含まれており、 Ubuntu の開発に大活躍しています。
しかし、 Launchpad は github や BitBucket とは異なり、ユーザー主体ではなくプロジェクト主体になっています。ユーザーディレクトリ配下にブランチを作ることも可能なのですが、それだとマージリクエストなどの管理ができないので、本当に他人に使ってもらいたいならまずプロジェクトを作るところから始めないといけなくて面倒です。
その他にも、 Ubuntu の開発には Launchpad の機能が必要なんだろうけど、個人ユーザーには github の機能のほうが魅力的という場面がよくあります。 Launchpad で満足していたとしても、 github で開発されているプロジェクトに参加するにはやはり github を使えなければなりません。
ということで、前置きが長くなりましたが、今日は bzr-git について説明して行きます。
先に要点をまとめておきます。

  • push ではなく dpush を使う
  • git@github.com:user/repo.git ではなく git+ssh://git@github.com/user/repo.git で場所を指定する

bzr-git のインストール

Windows であれば、インストーラーで git プラグインにチェックを入れればインストールできるはずです(未確認)。
Ubuntu の最新版をお使いであれば、 "sudo apt-get install bzr-git" で終わりです。Fedoraも、確認していませんが、きっと最新安定版が yum でインストールできると思います。
そうでない場合は、 dulwich という git リポジトリやプロトコルを扱うライブラリと、 bzr-git プラグインのインストールが必要です。
dulwich のインストールですが、 http://www.samba.org/~jelmer/dulwich/ から最新版の tar.gz をダウンロードするかURLをコピーして、 easy_install か pip install してください。
この記事を書いている12/24現在、最新版は 0.8.2 なので、次のようなコマンドでインストールできます。

$ easy_install http://www.samba.org/~jelmer/dulwich/dulwich-0.8.2.tar.gz

dulwich は pure Python でも動作しますが、C言語で書かれた拡張を利用したほうが高速なので、Debian系であれば "sudo apt-get install python-dev", RedHat系であれば "sudo yum install python-devel" のように、ヘッダファイルのインストールをしたほうが良いでしょう。
次に、bzr-git のインストールです。 https://launchpad.net/bzr-git の右側にある Downloads リンクに最新版の tar.gz へのリンクがあるので、それを先ほどの dulwich と同じように easy_install でインストールするか、ユーザーごとのプラグインディレクトリに次のような手順でインストールします。

$ mkdir -p ~/.bazaar/plugins/
$ cd ~/.bazaar/plugins/
$ wget http://launchpad.net/bzr-git/trunk/0.6.6/+download/bzr-git-0.6.6.tar.gz # 12/24時点での最新版
$ tar xf bzr-git-0.6.6.tar.gz  # bzr-git-0.6.6 というディレクトリができる
$ mv bzr-git-0.6.6 git         # git/ というディレクトリにリネーム

インストールできているかどうか確認しましょう。

$ bzr plugins -v
...
git 0.6.6
  A GIT branch and repository format implementation for bzr.
   /home/inada-n/.bazaar/plugins/git
...

push と dpush

実際に bzr-git を使う前に、 dpush コマンドについて説明しておきます。
bzr-git や bzr-svn で問題になるのは、異なるバージョン管理システム間でのリビジョンのラウンドトリップ(可逆変換)です。
ある bzr ブランチ上でコミットすると、 bzr のリビジョンができます(=X)。それを bzr-svn で svn のブランチに push すると、 svn 上のリビジョンに変換されます(=X')。その svn 上のリビジョンを他の bzr ブランチから pull したときに、もとの X に戻ればラウンドトリップができていて、別の X'' になってしまった場合はラウンドトリップができていません。
ラウンドトリップができるかどうかは重要な問題です。手元の最新リビジョンが X だったときに、リモートの最新リビジョン X' が X に変換できないと、手元のブランチがリモートの最新リビジョンと一致しているのかどうかを判断するには、別に X と X' が一致しているという対応表を管理しないといけなくなり、この対応表が無いと作業できなくなってしまいます。全員が同じ対応表を共有しないといけないのでは、分散バージョン管理の魅力が半減してしまいます。
bzr-git では、現在のところ、 git -> bzr -> git のラウンドトリップはできていますが、 bzr -> git -> bzr ができていません。つまり、 bzr のブランチを git で管理するには、 bzr のリビジョンを全部 git のリビジョンへと非可逆変換した後、その git のリビジョンから bzr のリビジョンを作りなおす操作が必要です。
この、非可逆変換を行いながら別のバージョン管理システムへ push するコマンドが dpush です。 bzr -> git -> bzr のラウンドトリップができていないので、 git への push はできず、 dpush のみが可能です。

bzr のブランチを github に dpush しよう

サンプルとして、 TortoiseBZR のブランチを github に置いてみます。準備として、次のように bzr ブランチを用意します。

$ bzr branch lp:tortoisebzr tbzr; cd tbzr

https://github.com/ を開いてください。右下の方に New repository というボタンがありますね?なかったら github アカウントを取得してください。このボタンを押して、 Create New Repository というフォームが出てきます。今回は TortoiseBZR のブランチを github に置いてみるので、 Project Name に "TortoiseBZR" と書いて、 "Create repository" というボタンを押します。これで methane/TortoiseBZR というリポジトリができました。名前を記入するだけでリポジトリが作れちゃうお手軽さが素敵です。
リポジトリができたら、そのリポジトリの使い方を説明したページが表示されます。そこで、git の push/pull 先として表示されている "git@github.com:methane/TortoiseBZR.git" が重要です。
git から push/pull するときは相手先が git だと判っているのでこれだけでいいのですが、 bzr からアクセスするときは "ssh経由のgitプロトコル" であることを教えてあげなければなりません。先ほどのリポジトリの場所を bzr 形式にすると、 "git+ssh://git@github.com/methane/TortoiseBZR.git" になります。先頭に "git+ssh://" が付いているだけじゃなく、 github.com の後ろを : から / にしていることに注意してください。
dpush する前に、 dpush によって bzr のリビジョンが git 用に書き換えられている事を確認するために、現在の bzr のリビジョンIDを見ておきましょう。

$ bzr log --show-ids -l3
...
revision-id: xxxxxx@yyyyy.zzz-20111207050015-p8yk5czl0re057lo
...

bzr のリビジョンは、デフォルトではリビジョンIDはコミッタのメールアドレス+タイムスタンプ+乱数になっています。さて、今度こそ dpush してみましょう。

$ bzr dpush git+ssh://git@github.com/methane/TortoiseBZR.git --remember

初回だけ、すべてのリビジョンを git 形式に変換しているので、少し時間がかかります。といっても TortoiseBZR の規模で数十秒なので、ラーメンを作って待つほどではありません。
さて、リビジョンIDはどうなっているでしょうか。

$ bzr log --show-ids -l3
------------------------------------------------------------
revno: 436 [merge]
revision-id: git-v1:962e312abbe8396cacdeaad1266b0269d160556a
parent: git-v1:146563a9e1c4324f190e5c3a9becc592c130a0e9
parent: git-v1:47b8db80c88b5065080397dc242294829973a326
git commit: 962e312abbe8396cacdeaad1266b0269d160556a
...

"git-v1:" という形式になっていますね。bzrのリビジョンIDはユニークであればなんでも良いので、gitのコミットIDを使うことで簡単に対応が取れるようになっています。
このあとは、コミットして dpush するたびに、新しいリビジョンがgit用に変換されます。
注意しないといけない点として、一度 dpush してリビジョンIDがgit用になってしまうと、もう Lauchpad 上の bzr ブランチとは全くの別物になってしまっています。 TortoiseBZR のブランチを改造して github に公開して「マージして」と言われても開発者は手動でマージするしかありません。
Launchpad と github を両方使う場合は、 github 側をマスター、 Launchpad 側をミラーという扱いにして、 Launchpad 上には git 用に変換されたリビジョンだけを置くようにすると良いでしょう。そうすると、github上の pull request はそのまま処理できますし、 Launchpad 上のマージリクエストも一旦手元でマージして github に dpush したあと Launchpad に push することができます。

github 上のブランチを bzr-git でローカルに持ってくる

ここまで来たら後は簡単です。先ほどの methane/TortoiseBZR を bzr-git で取得しましょう。

$ bzr branch git+ssh://git@github.com/methane/TortoiseBZR.git tbzr

tbzr というディレクトリがローカルブランチです。あとは先程と同じく、ここで作業して dpush していくだけです。

bzr-git の制限と今後

本当は bzr-git で git の作業ツリーを操作する紹介もしたかったのですが、まだ不安定でコミット時にエラーが出たりするのでやめておきます。この問題がなくなれば、作業ツリーを git にすることで git と bzr 間のリビジョンの変換によるオーバーヘッドが無くなるので、大きいリポジトリを最初に branch してくる時間が短縮されるはずです。
また、 bzr-2.4 + bzr-git-0.6.6 の段階では、 git の master ブランチしか扱うことができません。bzr-2.5からは1つのディレクトリに複数のブランチを格納する colocated branch が実験的に導入され、bzr-git もそれに合わせて git の master 以外のブランチが扱えるように拡張される予定です。
これらの改良が行われると、bzr-git は、 git の独特のコマンド体系を覚えなくても使える、しかも Windows や Mac OS X で日本語ファイル名を使ってもちゃんと NFC の UTF-8 でファイル名をコミットしてくれる、一番使いやすい github クライアントになるでしょう。

このブログに乗せているコードは引用を除き CC0 1.0 で提供します。