サブモジュール: コア コンセプト、ワークフロー、ヒント

Nicola Paolucci
Nicola Paolucci
リストに戻る

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

目次

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

コア コンセプト

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

サブモジュールは、ブランチ、参照、その他の記号参照ではなく、親プロジェクトで指定された正確なコミットによって追跡されます。

サブモジュールは、サブモジュールによって指定されたリポジトリが更新されても自動で更新されることは一切なく、親プロジェクト自体が更新された場合にのみ自動で更新されます。前述の Pro Git の章で非常に明確に表現されています。

その [submodule] サブディレクトリ内で変更とコミットを行うと、スーパープロジェクトはそこにある HEAD が変更されたことを認識して、現在作業しているコミットを正確に記録します。こうすることで、他の人がこのプロジェクトをクローンした際に環境を正確に再作成できるようになります。

言い換えれば

[...] git サブモジュール [...] は静的です。非常に静的です。git サブモジュールでは特定のコミットを追跡しています。ブランチでも参照でもなく、単一のコミットです。サブモジュールにコミットを追加しても、親プロジェクトでは認識されません。モジュールのフォークがたくさんあっても、git サブモジュールでは配慮されません。1 つのリモート リポジトリがあり、1 つのコミットを指しています。この場合、親プロジェクトを更新するまで何も変わりません。

考えられるワークフロー

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

  • コンポーネントまたはサブプロジェクトの変更が速すぎる、または今後の変更によって API が壊れる場合は、安全のためにコードを特定のコミットにロックできます。

  • あまり頻繁に更新されないコンポーネントがあり、それをベンダーの依存関係として追跡する場合があります。たとえば、私はこれを vim プラグインのために行います。

  • プロジェクトの一部をサード パーティに委任して、その作業を特定の時間またはリリースで統合する場合。これも、更新があまり頻繁でない場合に機能します。

よく説明されたシナリオに対する finch へのクレジット

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

サブモジュール インフラストラクチャは強力で、コードベースの分離と統合に役立ちます。ただし、手順が合理化されていない、またはコマンド ライン ユーザー インターフェイスが強力にサポートされていない単純な操作もあります。

プロジェクトで git サブモジュールを使用する場合は、こういう事態に遭遇したことがある、もしくはこれから遭遇することになります。その場合は解決策を調べる必要があります。何度も繰り返さなければなりませんが、調べる時間を節約する方法があります。InstapaperEvernote、または古いやり方ですがブックマークを使用してこのページを保存してください (:D:D)。そうすればしばらくの間は安泰です。

というわけで、ここでご説明いたします。

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

これは非常に一般的なワークフローです。すなわち、他の誰かのプロジェクトをサブモジュールとして使い始めますが、しばらくするとカスタマイズして自分で微調整する必要があることに気付きます。このため、プロジェクトをフォークしてこのサブモジュールを自分のフォークに置き換えたくなります。どうすれば可能でしょうか?

サブモジュールは .gitmodules に格納されています。

$ cat .gitmodules [submodule "ext/google-maps"] path = ext/google-maps url = git://git.naquadah.org/google-maps.git

URL をテキスト エディターで編集して、次を実行するだけです。

$ git submodule sync

これで、このサブモジュール リストのコピーが含まれる .git/config が更新されます (また、.git/config の関連する [submodule] セクションのみを手動で編集できます)。

Stack Overflow の記事をご参照ください。

サブモジュールの削除方法

これはかなり一般的なニーズですが、やや複雑な手順があります。サブモジュールを削除するには、次を行う必要があります。

  1. 関連する行を .gitmodules ファイルから削除します。
  2. 関連するセクションを .git/config から削除します。
  3. git rm --cached path_to_submodule を実行します (末尾のスラッシュなし)。
  4. 現在追跡されていないサブモジュール ファイルをコミットして削除します。

    Stack Overflow の記事をご参照ください。

サブモジュールをプロジェクトに統合するにはどうしたらいいですか?

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

  1. サブモジュールの参照をインデックスから削除しつつファイルは残します。

    git rm --cached submodule_path (末尾のスラッシュなし)
  2. .gitmodules ファイルを削除します。あるいは、複数のサブモジュールがある場合は次のファイルを編集して、該当するサブモジュールをリストから削除します。

    git rm .gitmodules
  3. .git メタデータ フォルダーを削除します (念のためバックアップをとっておきます)。

    rm -rf submodule_path/.git
  4. submodule メイン リポジトリのインデックスにを追加します。

    git add submodule_path git commit -m "remove submodule"

注: 上で概説した手順は、サブモジュールの履歴を破壊します。整合性のあるサブモジュールの履歴を保持する場合は、手が込んだ "マージ" を行う必要があります。詳細は、総合的な Stack Overflow の記事をご参照ください。

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

submodules が単独で dirty になることがあります。たとえば、git submodules によって vim プラグインを追跡する場合は、helptags などのローカル ファイルが生成または変更される可能性があります。残念ながら、あなたがこれらの変更に一切興味がなくコミットするつもりはないとしても、今度は git status のせいで悩むことになります。

解決策は非常に簡単です。.gitmodules ファイルをリポジトリのルートで開いて、無視するサブモジュールごとに次の例のように ignore = dirty を追加します。

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

すばらしい説明をしてくれた Nils に感謝します。

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

kernel.org に関する Git Submodule チュートリアルからわかるように、リモート リポジトリとやり取りする際に注意すべき重要な点がいくつかあります。

1 つ目は、サブモジュールの変更を参照するスーパープロジェクトに変更を公開する前に、必ずサブモジュールの変更を公開することです。この変更が他のユーザーがリポジトリをクローンするのを妨げる可能性があるため、これは非常に重要です。

2 つ目は、git submodule update の実行前にすべての変更をコミットすることを忘れないことです。何らかの変更があったとしても上書きされるためです。

結論

これらの注意事項を参考にすれば、サブモジュールを使用する際に多くの一般的な繰り返しワークフローに取り組むことができるようになるはずです。今後の投稿では、git submodule に代わるものについて書くつもりです。

DVCS を使いこなす方法の詳細は、私 (@durdn) か @Bitbucket チームをフォローしてください。

Git を学習する準備はできていますか?

この対話式チュートリアルを利用しましょう。

今すぐ始める