依存リポジトリ管理でのsubmodule/subtree/subrepoの使い分け

  • 57
    いいね
  • 0
    コメント
この記事は最終更新日から1年以上が経過しています。

依存ライブラリを利用する場合RubyGemsやらCocoaPodsといったツールで万事解決するケースがほとんどだと思いますが、たまーにGitに上がっているライブラリを直接自分のリポジトリに追加しないといけない場合もあったりします。

こういった時に使うGitのサブコマンドそれぞれの特徴と使いどころをまとめてみました。

submodule

一番スタンダードな外部リポジトリ追加方法です。たぶん大抵の依存管理ではこれを使えば十分でしょう。git-submoduleを利用すると、外部リポジトリのコード自体は自プロジェクトの管理下に取り込まれず、リポジトリの特定コミットへの参照情報のみが登録されます。外部リポジトリのcommit hashへのポインタが追加されるようなイメージです。

$ git submodule add git@github.com:Alamofire/Alamofire.git
$ git diff --cached Alamofire
      1 diff --git a/Alamofire b/Alamofire
      2 new file mode 160000
      3 index 0000000..3382b46
      4 --- /dev/null
      5 +++ b/Alamofire
      6 @@ -0,0 +1 @@
      7 +Subproject commit 3382b46170901f3c0050cd2dd31d7b07b2b7b7b0

submoduleとして追加した外部リポジトリがあるはずのディレクトリは、初期状態では空っぽです。git submodule initgit submodule updateを実行すれば外部リポジトリのコードの実体をローカル上に落としてくる事ができ、ライブラリを自プロジェクト側から利用できるようになります。ただし、あくまでsubmoduleは外部リポジトリへの参照なので、submodule updateで取得したコードは自プロジェクトの管理下には置かれません。そのためファイルに修正を加えてcommitすることもできません。あくまで外部リポジトリを参照するだけで、外部リポジトリに一切変更を加えない場合にはsubmoduleを使うと良さそうです。

subtree

外部リポジトリのライブラリを利用する際に、プロジェクト固有の修正をライブラリに加えたい場合があります。例えばプロジェクトで利用している言語・フレームワークのバージョンに合うようライブラリのビルド設定を変えるとか。けっこうレアケースだとは思いますが、こういった修正は外部リポジトリには反映したくないような場合が多いと思います。

git-subtreeは、submoduleと異なり外部リポジトリのファイルを自リポジトリの一部として取り込むことができます。

$ git remote add alamofire git@github.com:Alamofire/Alamofire.git
$ git subtree add --prefix Libraries/Alamofire alamofire master --squash

subtreeでは外部リポジトリのファイルも自リポジトリの一部として扱われるため、自リポジトリのコード同様に修正・commitすることができます。またsubtreeは依存リポジトリのファイルだけでなく、コミットツリーも自リポジトリに取り込むような形になります。なので—squashオプションを付け忘れると、自リポジトリに外部リポジトリのhistoryが全て混ざってしまうので要注意。

一見外部リポジトリのコードを全てgit addしただけかのような見た目になりますが、submodule同様に外部リポジトリの更新は適宜取り込むことができます。

$ git pull -s subtree --squash alamofire master

この時も--squashを付けないと外部リポジトリの履歴が取り込まれるので忘れずに。

subrepo

subtreeではsubmoduleにできなかった外部リポジトリへの変更が可能になっていますが、外部リポジトリのファイルやコミットツリーを自リポジトリに取り込む形となり、squashし忘れると大変なことになったりと若干扱いが面倒です。ここらへんの微妙な問題を取り払うため、ingydotnet/git-subrepoが公開されています。

subrepoで外部リポジトリを追加する例です。

$ git subrepo clone git@github.com:Alamofire/Alamofire.git Libraries/alamofire

外部リポジトリのファイルの実体はsubtree同様に自リポジトリの管理下に置かれますが、subtreeのようにsquashしないとhistoryごと取り込まれるといったことはありません。git subrepo pullで外部リポジトリの更新を取り込む際も同様です。subtree同様自リポジトリ固有のファイル修正を加えつつ、submoduleのように見かけ上外部リポジトリを独立したリポジトリとして扱うことができます。gitに内蔵されたサブコマンドではないため複数人で利用する際は面倒ですが、こういうやり方もあるよという事で。

まとめ

Gitで外部の依存リポジトリを追加する手段として、シンプルなsubmodule、かゆいところに手が届くsubtree、submodule・subtreeのいいとこ取りとしてのsubrepoの特徴をまとめました。ほとんどの場合submoduleだけで十分だと思いますが、利用シーンに合わせてこれらを使い分けていけば依存管理がスマートになって良いと思われます。

この投稿は Git Advent Calendar 201421日目の記事です。