Git subtree を使うとリポジトリを他のリポジトリのサブディレクトリとして追加することができます。Git プロジェクトがプロジェクト依存関係を管理する方法はいくつかありますが、これはその一つです。記憶力の良い人は、私が以前に Git submodule の代替の記事でこのコマンドの使用法と利点について書いたことを覚えているでしょう。

Git subtree の基本

それでは git subtree があなたにとって有益かどうかを判断するため、基本を振り返ってみましょう。想像して下さい。あなたは自分の持つリポジトリに外部プロジェクトをいくつか追加したいが、あなたや同僚の日々の利用するものにたくさんのものを追加したくはありません。このような場合に subtree コマンドは役立ちます。

例えば vim 設定を保存しているリポジトリに vim 拡張を追加したい場合はこのようにします:

Screen Shot 2015-09-02 at 1.54.15 PM

このコマンドは、vim-fireplace プロジェクトの全履歴をフォルダー .vim/bundle/fireplace にまとめると同時に、今後のために master の SHA-1 を記録します。squash でまとめられた git subtree add の結果は 2 つのコミットになります:

Screen Shot 2015-09-02 at 1.56.00 PM

あとでこのサブフォルダを子リポジトリの最新版に更新したい場合は、subtree pull に同じパラメータを付けて実行します:

Screen Shot 2015-09-02 at 2.09.50 PM

基本的な使用法は以上です。さらに慎重かつ計画的に行いたい場合は、子プロジェクトのタグを付けたリビジョン (v1.0 など) のみ addpull を行うと良いでしょう。こうすることで、まだ安定版ではないかもしれない master からコードをインポートすることが防げます。

注意: git-subtree は sub-project のコミット ID を保存しますが meta-data の refs は保存しません。しかしコミット ID (sha-1) が与えられているためこれは問題ではありません。ls-remote のようなコマンドによってコミットに関連付けられているシンボリック名を確認することができます:

Screen Shot 2015-09-02 at 2.11.03 PM

Git subtree エイリアス

subtree コマンドを頻繁に使用する場合、$HOME/.gitconfig に簡単なエイリアスをいくつか用意することで、コマンドを短縮し簡素化することができます:

Screen Shot 2015-09-02 at 2.11.39 PM

私は scp コマンド (scp <remote src> <dest>) のようにしてサブツリーを追加したいので、私が使用しているエイリアスはパラメーターの順番を反対にしています。これは以下のように使用します:

Screen Shot 2015-09-02 at 2.12.13 PM

git subtree の内部で起こっていること

私は最近 git-subtree の実装を見たのですが、それはとても賢く実装されていました。まず感心したのは、よく知っていたことですが、Git subtree はシェルスクリプトとして実装されていて、しかもそれがとても読みやすいものだったのです。

そのコマンドの核となるテクニックはこうです。git-subtree は、コミットに直接インポートするコードの外部 meta-data を保存します。例えば squashed pull (まとめられたプル) は、マージ前にこれら 2 つの値をコミットメッセージに保存します:

Screen Shot 2015-09-02 at 2.12.49 PM

git-subtree-split フィールドには、git-subtree-dir フォルダーに追加されていたサブプロジェクトのコミット ID (sha-1) を記録しています。シンプルですね! この情報を使うことで、次に実行される git subtree pull が以前に統合した時点のものを引き出し、次回の squash や merge のベースとして使用することができます。

git subtree 以降のリベース

あなたは sub-tree の含まれるリポジトリをどうやってリベースしていますか? この Stack Overflow に関する議論から分かることは、銀の弾丸は存在しないということです。

有効な手順は、手動でリベースを行うために単純に rebase --interactive を実行し、git subtree add のコミットを削除して rebase --continue を実行し、リベースが完了したら git subtree add コマンドを再実行することだと思います。

git-subtree のハック

小さなことですが、私が気づいたデフォルトのコマンドに欠けているものは、追加するオリジナルリポジトリの URL が保存されないということです。最近 vim 拡張を全て更新しようとしていた時にこのことを思い出しました。私は、git subtree add を使用してこれまでに追加していたソースリポジトリの URL を全て忘れてしまっていました。

私は、Git Merge 2015 に参加して以来、このプロジェクトに貢献する方法を熱心に探していたため、自分自身に「文句を言うのではなく自分なら改善できるはずだ!」と言い聞かせました。

そこで私は git-subtree.sh スクリプトに追加機能を実装しました。

私は git subtree add が squash でまとめられたコミットに情報を追加できるように git-subtree-repo という拡張フィールドを追加しました。次の通りです:

Screen Shot 2015-09-02 at 2.14.10 PM

拡張フィールド付きのコミット結果はこのようになります:

Screen Shot 2015-09-02 at 2.15.54 PM

この比較的小さな追加により、他のリポジトリから追加された全フォルダーの一覧を表示する新しい subtree コマンドを実装することができるようになりました:

Screen Shot 2015-09-02 at 2.16.46 PM

出力はこのようになります:

Screen Shot 2015-09-02 at 2.17.38 PM

そして再度 sha-1ref に関連づけるために git ls-remote を使用できます:

Screen Shot 2015-09-02 at 2.18.13 PM

以上は現在作成中ですが、私の Git fork から試すことができます。

まとめ

この変更に対する適正かつ厳格なテストが終わり次第、git のメーリングリストにパッチを投稿し、これが有益と判断されるかどうかを確認する予定です。きっと大丈夫でしょう! とにかく皆さんがこのブログ記事を楽しんで頂けたら幸いです。Git についてもっと興味がありましたら、私 @durdn@atlassiandev にお知らせ下さい。

6 月 9 日〜 11 日にプラハで開催される AtlasCampNicola Paolucci の講演をご覧下さい。一緒にビールを飲みましょう。



*本ブログは Atlassian Developers の翻訳です。本文中の日時などは投稿当時のものですのでご了承ください。
*原文 : 2015 年 5 月 18 日 “The power of Git subtree