サブモジュール: コア コンセプト、ワークフロー、ヒント
Nicola Paolucci
開発者アドボケート
目次
関連資料
Git リポジトリ全体を移動する方法
ソリューションを見る
Bitbucket Cloud での Git の使用方法についてのチュートリアルです。
コア コンセプト
サブモジュールになじんでもらうため、まずはコア コンセプトを簡単に説明します。
サブモジュールは、ブランチ、参照、その他の記号参照ではなく、親プロジェクトで指定された正確なコミットによって追跡されます。
サブモジュールは、サブモジュールによって指定されたリポジトリが更新されても自動で更新されることは一切なく、親プロジェクト自体が更新された場合にのみ自動で更新されます。前述の Pro Git の章で非常に明確に表現されています。
その [submodule] サブディレクトリ内で変更とコミットを行うと、スーパープロジェクトはそこにある HEAD が変更されたことを認識して、現在作業しているコミットを正確に記録します。こうすることで、他の人がこのプロジェクトをクローンした際に環境を正確に再作成できるようになります。
[...] git サブモジュール [...] は静的です。非常に静的です。git サブモジュールでは特定のコミットを追跡しています。ブランチでも参照でもなく、単一のコミットです。サブモジュールにコミットを追加しても、親プロジェクトでは認識されません。モジュールのフォークがたくさんあっても、git サブモジュールでは配慮されません。1 つのリモート リポジトリがあり、1 つのコミットを指しています。この場合、親プロジェクトを更新するまで何も変わりません。
考えられるワークフロー
コア コンセプトを念頭に置きながら考えてみると、submodule
がうまくサポートできるワークフローがあればそれほどうまくいかないワークフローもあることがわかるでしょう。サブモジュールがよい選択だと言えるシナリオが少なくとも 3 つあります。
-
コンポーネントまたはサブプロジェクトの変更が速すぎる、または今後の変更によって API が壊れる場合は、安全のためにコードを特定のコミットにロックできます。
-
あまり頻繁に更新されないコンポーネントがあり、それをベンダーの依存関係として追跡する場合があります。たとえば、私はこれを vim プラグインのために行います。
-
プロジェクトの一部をサード パーティに委任して、その作業を特定の時間またはリリースで統合する場合。これも、更新があまり頻繁でない場合に機能します。
よく説明されたシナリオに対する finch へのクレジット。
初めての人向けの役に立つコツ
サブモジュール インフラストラクチャは強力で、コードベースの分離と統合に役立ちます。ただし、手順が合理化されていない、またはコマンド ライン ユーザー インターフェイスが強力にサポートされていない単純な操作もあります。
プロジェクトで git サブモジュールを使用する場合は、こういう事態に遭遇したことがある、もしくはこれから遭遇することになります。その場合は解決策を調べる必要があります。何度も繰り返さなければなりませんが、調べる時間を節約する方法があります。Instapaper、Evernote、または古いやり方ですがブックマークを使用してこのページを保存してください (: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]
セクションのみを手動で編集できます)。
サブモジュールの削除方法
これはかなり一般的なニーズですが、やや複雑な手順があります。サブモジュールを削除するには、次を行う必要があります。
1. 関連する行を .gitmodules
ファイルから削除します。
2. 関連するセクションを .git/config
から削除します。
3. git rm --cached path_to_submodule
を実行します(末尾のスラッシュなし)。
4. 現在追跡されていないサブモジュール・ファイルをコミットして削除します。
サブモジュールをプロジェクトに統合するにはどうしたらいいですか?
換言すると、サブモジュールをサブモジュールでなくする方法です。やりたいことがサブモジュールのコードを主リポジトリに入れるだけであれば、サブモジュールを削除してそれらのファイルを主リポジトリに追加するだけでよいのです。
1. サブモジュールの参照をインデックスから削除しつつファイルは残します。
git rm --cached submodule_path (no trailing slash)
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 チームをフォローしてください。
この記事を共有する
次のトピック
おすすめコンテンツ
次のリソースをブックマークして、DevOps チームのタイプに関する詳細や、アトラシアンの DevOps についての継続的な更新をご覧ください。