Git Submodules: コアコンセプト、ワークフロー、コツ

Git を使った開発では、サブモジュールを使うことによって、他のプロジェクトを自分のコードベースに取り込めるようになります。それも、他のプロジェクトの履歴を分離しつつ、あなたのプロジェクトの履歴と同期できるようになるのです。これはベンダーライブラリ問題や依存関係問題を解決する便利な方法です。git に関してはいつもそうですが、このアプローチもかなり自己流なのでうまく出来るようになるまで少しばかり研究することをお勧めします。submodules に関する好例詳しい説明はすでに公開されているので、ここでまた繰り返すのはやめることにします。この記事では submodule という機能を最大限に活用するのに役立つであろういくつかの面白い情報を共有したいと思います。

目次

  1. コアコンセプト
  2. 考えられるワークフロー
  3. 初めての人向けの役に立つコツ
  4. サブモジュールを自分のフォークしたリポジトリで置き換える方法
  5. サブモジュールを削除する方法
  6. サブモジュールを自分のプロジェクトに統合し直す方法
  7. サブモジュールの変更を無視する方法
  8. 危険地帯!リモートリポジトリとの連携の落とし穴
  9. まとめ

コアコンセプト

サブモジュールになじんでもらうため、まずはコアコンセプトを簡単に説明します。

サブモジュールとは親のプロジェクトの特定のコミットに紐づくものです。ただし、ブランチでも参照でも何らかのシンボリックリファレンスでもありません。

サブモジュールで指定されたリポジトリが更新されたとしても、サブモジュールが自動的に更新されることは決してありません。サブモジュールが更新されるのは、親プロジェクト自体が更新されたときだけです。このことについては、以前 Pro Git の章ではっきりと述べています。

サブディレクトリ(サブモジュール)を変更してコミットすると、親プロジェクトは HEAD が変更されたことを検知して、そこで行われたコミットを記録します。こうすることで、誰かがこのプロジェクトのクローンを作った場合にも、同じ環境を正確に再作成できるのです。

あるいは、言い換えると

(前略)git のサブモジュールは(中略)静的なものです。極めて静的なのです。git のサブモジュールが紐づいているのは特定のコミットであり、ブランチでも参照でもありません。単なるコミットなのです。サブモジュールに何かコミットしても、親プロジェクトにはわかりません。あなたのモジュールがどれだけフォークされていても git のサブモジュールはまったく気にしません。リモートリポジトリが一つあり、とそれを指し示すコミットが一つあるだけなのです。親プロジェクトを更新するまで何も変更されることはありません。

考えられるワークフロー

コアコンセプトを念頭に置きながら考えてみると。submodule がうまくサポートできるワークフローがあれば、それほどうまくいかないワークフローもあることがわかるでしょう。サブモジュールがいい選択だと言えるシナリオが少なくとも三つあります。

  • コンポーネントやサブプロジェクトの変更があまりにも早すぎたりやってくる変更がAPIを壊してしまったりする場合、自衛のためにコードを特定のコミットで固定することができます。
  • ベンダーライブラリの依存関係のように、ほとんど更新されないがそのコンポーネントを追跡したい場合。私は自分の vim plugins について、こうしています。
  • プロジェクトの一部をサードパーティーに委譲して、特定のタイミングやリリースの際に統合したい場合。繰り返しになりますが、これがうまくいくのはそれほど更新が激しくない場合だけです。

この分かりやすいシナリオを考えてくれた人物としてフィンチ氏挙げておきます。

初めての人向けの役に立つコツ

サブモジュールの強力なインフラストラクチャはコードベースを分離・統合するのに役立ちます。 そのためには単純ながらいくつか操作が必要で、効率的な手順もコマンドラインインターフェイスのサポートもありません。

あなたのプロジェクトでサブモジュールを使っているなら、すでにそれらを行っているか、将来行うことになるでしょう。いざ取り組むことになったならそのためのやり方を探さなければなりません。それも1回きりではなくて何度も。そこであなたが調査する時間を節約してあげましょう。InstapaperEvernote あるいは昔ながらのブックマークにこの記事(:D)を記録してください。それくらいはすぐに終わるでしょう。

さぁ、あなたが探し求めている情報はこちらです。

サブモジュールを自分のフォークしたリポジトリで置き換える方法

ごく一般的なワークフローです。初めは誰かのプロジェクトをサブモジュールとして使っていたところに、後から自分でそれをカスタマイズすることになった場合、元のプロジェクトをフォークしてサブモジュールを置き換えたくなるでしょう。どうすればいいでしょうか?
サブモジュールは .gitmodules に格納されています。


1
2
3
4
$ cat .gitmodules
[submodule “ext/google-maps”]
path = ext/google-maps
url = git://git.naquadah.org/google-maps.git

エディタで url を編集して次のコマンドを実行しましょう。


1
$ git submodule sync

そうすると .git/config が更新されます。.git/config にはサブモジュールのリストのコピーが格納されています。( .git/config ファイルの [submodule] セクションを直接編集することもできます。)
Stack Overflowの記事も参照してください

サブモジュールを削除する方法

よく必要になることですが少々複雑な手順になります。サブモジュールを削除するには次のようにします。

  1. .gitmodules ファイルから該当する行を削除します。
  2. .git/config から該当するセクションを削除します。
  3. git rm –cached path_to_submodule を実行します。(パスの末尾にスラッシュは不要です)
  4. コミットして、使わなくなったサブモジュールのファイルを削除します。

Stack Overflowの記事も参照してください

サブモジュールを自分のプロジェクトに統合し直す方法

別の言い方をするなら、サブモジュールをサブモジュールではなくする方法です。やりたいことが、サブモジュールのコードを主リポジトリに入れるだけであれば、サブモジュールを削除してそれらのファイルを主リポジトリに追加するだけでよいのです。

  1. サブモジュールの参照をインデックスから削除しつつファイルは残します。
    
    
    1
    git rm --cached submodule_path (no trailing slash)
  2. .gitmodules ファイルを削除します。あるいは、複数のサブモジュールがある場合は以下のファイルを編集して、該当するサブモジュールを一覧から削除します。
    
    
    1
    git rm .gitmodules
  3. 念のためバックアップをとってからサブモジュールの .git フォルダ(メタデータ)を削除します。
    
    
    1
    rm -rf submodule_path/.git
  4. 主リポジトリのインデックスに submodule を追加します。
    
    
    1
    2
    git add submodule_path
    git commit -m "remove submodule"

注意:この手順はサブモジュールの履歴を無かったことにしてしまいます。履歴を残したければ “merge” してください。詳細についてはこちらのStack Overflowの記事に詳しく書いてあります。

サブモジュールの変更を無視する方法

あなたの submodules 自体が dirty になってしまうこともあります。例えば vim のプラグインを追跡するのに submodules を使っている場合、 helptags ファイルなどのように、ローカルに作成したりローカルで変更したりするものが出てきます。そういった変更について git status があなたを困らせるようになるでしょう。それらの変更に全く興味は無いしコミットする気も無いのだとしてもです。

解決するのは簡単です。リポジトリの最上位ディレクトリにある .gitmodules を開いて変更を無視したいモジュールについて ignore = dirty と追加してあげればいいのです。例を示します。


1
2
3
4
[submodule ".vim/bundle/msanders-snipmate"]
  path = .vim/bundle/msanders-snipmate
  url = git://github.com/msanders/snipmate.vim.git
  ignore = dirty

ニルス氏の素敵な解説に感謝します。

危険地帯!リモートリポジトリとの連携の落とし穴

kernel.org で読むことのできる Git サブモジュールチュートリアルにはリモートリポジトリと連携する上でのいくつかの大事な注意事項が書かれています。

第一は、どんな時でもサブモジュールを参照している親プロジェクトの変更を公開する前にサブモジュールの変更を公開する、というものです。リポジトリをクローンした人を困らせてしまうかもしれないので非常に重要です。

第二は、 git submodule update を実行する前に全ての変更をコミットすることを忘れないことです。何らかの変更があったとしても上書きされてしまうからです!

まとめ

こういった注意事項で理論武装しておけばよくあるワークフローでサブモジュールを使ったときに起きるであろう問題に対処できるはずです。次回の記事では git submodule の代替案について書こうと思います。

DVCS をもっと楽しく使うためにも私のアカウント(@durdn)や素晴らしい開発チームのアカウント(@AtlDevTools)をフォローしてください。

*本ブログは Atlassian Blogs の翻訳です。本文中の日時などは投稿当時のものですのでご了承ください。
*原文 : 2013 年 3 月 26 日 "Git Submodules: Core Concept, Workflows And Tips"
*翻訳協力: アトラシアンエキスパート
logo_g_gxp