言語ゲーム

とあるエンジニアが嘘ばかり書く日記

Twitter: @propella

git サブツリーマージを使って他のプロジェクトのソースコードを使う。

git で管理しているプロジェクトの中に他の git プロジェクトを混ぜる方法を書きます。例えば私の lispコンパイラ tamacola では、abcsx という別のレポジトリにあるアセンブラを使っているんだけど、これをライブラリとして使いたい。しかも単にコピーするだけじゃなくて、もしも abcsx を変更した時に、その変更点を元のレポジトリにも反映したい。そんな状況です。

そこで役立つのがサブマージツリーという仕組みです。普通 git では二つのプロジェクトを混ぜて一つのレポジトリを作りたいとき、一つのディレクトリに二つのプロジェクトが混ざってしまいます。サブマージツリーを使うと、ライブラリとして使いたい方のプロジェクトをサブディレクトリとしてマージする事が出来ます。

サブマージツリーを使った作業ツリーの作り方。

まず、あなたはとある git レポジトリ上で仕事をしているとします。abcsx の公開レポジトリにアクセスして abcsx-remote という名前のリモートブランチに取って来ます。リモートブランチというのは、URL に付けるあだ名の事で、これをやると URL を書く代わりにリモートブランチ名を書く事が出来ます。

git remote add abcsx-remote git://github.com/propella/abcsx.git
git fetch abcsx-remote

次に、リモートブランチの内容を abcsx-branch という作業用のブランチにチェックアウトします。

git checkout -b abcsx-branch abcsx-remote/master

これで、一つのディレクトリに別のプロジェクトが混ざった状態になります。メインのプロジェクトは master ライブラリのプロジェクトは abcsx-branchと別のブランチにあります。このままではルートディレクトリに二つのプロジェクトが混ざってしまっているので、abcsx-branch の内容を、master ブランチの中の abcsx ディレクトリに展開します。

git checkout master
git read-tree --prefix=abcsx/ -u abcsx-branch

これで完成。今後 master の abcsx/ で変更されたコードは、abcsx-branch のに戻ってオリジナルのレポジトリに反映させる事が出来ます。ここでややこしいのが、このサブマージツリーは作業用レポジトリの中だけの話で、これで作ったレポジトリをサーバにプッシュしてもサブマージツリーの情報まではプッシュされません。そこで、他のマシンで作業して再び abcsx の変更を反映させたい時はまたそれぞれ同じようにサブマージツリーを作ります。

別にチェックアウトしたツリーでもう一度サブツリーマージを作る。

書き込み用のレポジトリを登録してブランチを作ります。

git remote add abcsx-remote git@github.com:propella/abcsx.git
git fetch abcsx-remote
git checkout -b abcsx-branch abcsx-remote/master

ここで、どうやって abcsx ディレクトリの内容が再び abcsx-branch と結びつくのか良くわかりませんが、なぜかこれでうまく行きました。

変更の内容を上流に反映させる。

新しく作った abcsx-branch に移動して、master で変更があった内容を上流に更新します。このまま master をマージすると master の歴史が全部マージされてしまうので、--no-commit を使って余計な情報がマージされないようにします。

git merge --squash -s subtree --no-commit master

このあと git status などを使って確認した後、早速元のサーバにアップします。この文法が凶悪に難しいので注意して下さい。うっかり単に git push とやると死にます。git push サーバレポジトリ アップしたいブランチ:サーバブランチ のような文法を使います。ここでアップしたいブランチを書かないと master を選んだ事になってしまいぐちゃぐちゃになります。アップしたいブランチには、ブランチ名を書くか、HEAD のようなシンボルでも行けます。

git push abcsx-remote HEAD:master

上流の内容を自分のプロジェクトに反映させる。

これはまだやってないので機会が出来たら書きます。同じく git merge --squash -s subtree を使うらしいです。