git とプロジェクトの依存関係

Nicola Paolucci
Nicola Paolucci
リストに戻る

次の質問を考えてみてください。プロジェクトの依存関係を git でどう処理していますか。私たちのプロジェクトは互いに依存関係にある複数のリポジトリから構成されています。今はそうしたリポジトリを svn:externals で管理しています。これを git で処理する最適の方法は何でしょうか。git を使って、非常に大きなリポジトリを小さなコンポーネントに分割するにはどうすればよいでしょうか。これは最近行った Git を正しく理解する ツアーのヨーロッパレグで、最もよく出された質問を例としていくつかあげたものです。

このテーマは git を採用しているソフトウェア チームの多くが抱える悩みの種のようです。この記事ではこの問題に焦点を当ててみます。

プロジェクトの依存関係とビルド インフラストラクチャは互いに絡み合った 2 つの領域であることは明らかであり、アトラシアン社内でさえも「ビルドの未来」について議論が巻き起こりました。

複数のリポジトリが分かれている場合は、リポジトリが 1 つである場合と違って、難しい点がいくつか生じます。しかし、少なくとも次の 2 つの主な理由から、リポジトリが複数になるのは、ソフトウェア プロジェクトの発展過程では比較的自然なことであり、時にはそうならざるを得ない場合もあります。その 2 つの理由とは、ビルド時間の増加とプロジェクト間で共有する依存関係です。

対応の概要:ガイドラインと次善の解決策

では、質問に戻りましょう。git を使って、プロジェクトの依存関係をどのように追跡し、管理するのでしょうか

そんなことはしないにこしたことはないですよね!

冗談はさておき、まずはじめにをおおまかに回答し、後から深く掘り下げましょう。Git を使う場合でも、別のやり方の場合でも、プロジェクトの依存関係に関連するすべての問題を何の苦もなく解決する特効薬はないことを認識してください。

プロジェクトが特定のサイズを超えると、論理コンポーネントに分割するのが妥当です。しかし、1 つのリポジトリのコードが 1 億行を超えるのを待つ必要はありません。したがって、以下は独自のアプローチを考案するためのガイドラインにすぎません。

第一の選択:Git に代わる適当なビルド / 依存関係ツールを使用する

依存関係の管理ツールは、規模の大きいプロジェクトで増大していく手間とビルド時間に対処する方法として、私が現在推奨しているものです。

モジュールを個々のリポジトリに分けて保存し、そのジョブ用に構築されたツールを使用して、モジュール間の相互依存性を管理します。(ほぼ)すべてのテクノロジーに応じたツールが 1 つずつ用意されています。次に、いくつか例をあげます。

  • Maven (または Gradle): Java を使用している場合
  • Npm: ノード アプリ
  • BowerComponent.io など: Javascript を使用している場合 (更新しました!)
  • Pip、requirements.txt: Python を使用している場合
  • RubyGemsBundler: Ruby を使用している場合
  • NuGet: .NET を使用している場合
  • Ivy (または何らかのカスタム CMake アクション): C++ を使用している場合 (更新しました!)
  • CocoaPods: Cocoa iOS アプリ用
  • Composer または Phing: PHP 用 (追加しました!)
  • Go では、ビルド/依存関係インフラストラクチャの一部が言語に組み込まれています (より完全なソリューションを開発中です。「godep」を参照)。Git サーバー Bitbucket Server では、Maven と Bower の両方が使用されています。ビルド時に、選択したツールによって適切なバージョンの依存関係がプルされて、メイン プロジェクトがビルドされます。これらのツールの中には制限があり、最適ではない仮定を行うものもありますが、どれも実証済みで実用に供します。

プロジェクトを分割するのにかかる手間

簡単に言うと、プロジェクトの開始時には、すべてが 1 つのビルドにまとめられています。しかし、プロジェクトが大きくなると、これではビルドが遅くなりすぎることがあります。この時点で「キャッシング」が必要になり、ここが依存関係の出番です。ちなみにこれは、サブモジュール (下記参照) が動的言語に非常に適していることなどを意味します。基本的に、ほとんどの人はある時点でビルド時間を気にする必要があると思います。そのため、依存関係管理ツールを使用する必要があります。

コンポーネントを別々のリポジトリに分割するには、相当の手間がかかりす。順不同であげると、次のようになります。

  • コンポーネントに変更を加えると、リリースが必要になる
  • 時間がかかり、多くの愚かな理由で失敗する可能性がある
  • 小さな変更には適さない
  • コンポーネントごとに新しいビルドを手動で設定しなければならない
  • リポジトリの検索がしにくくなる
  • 単一のリポジトリですべてのソースが利用可能でない場合のリファクタリング
  • セットアップによっては(当社の場合など)、API の更新には、まず製品、次にプラグイン、さらに再び製品のマイルストーン リリースが必要になり、多分途中でいくつか見逃しが生じます。しかし、ここで気づきます。これでは問題の完全な解決には遠い、と。

第 2 の選択肢: git サブモジュールを使う

依存関係ツールを使用できない、または使用したくない場合、git にはサブモジュールを処理する機能があります。サブモジュールは、特に動的言語に便利です。ただし、ビルド時間を短縮できるとは限りません。私はこれまでにいくつかのガイドラインとヒントを書いており、代替案も検討しました。インターネット上でもサブモジュールに反対する意見が大半です

svn: externals と git の 1:1 の対応

しかし、svn: externalsgit を 1 対 1 で対応させたい場合は、サブモジュールを使用してサブモジュールリリース ブランチのみを追跡し、ランダムなコミットは追跡しないようにします。

第 3 の選択:他のビルドおよびクロススタック依存関係ツールを使う

完全に統一されていて、単一のツールを使用して構築され、構成されているプロジェクトばかり扱えるわけではありません。たとえば、一部のモバイル プロジェクトでは Java と C++ の依存関係を両立させたり、独自のツールを使用してアセットを生成したりする必要があります。こうした複雑な状況では、最上部に余分のレイヤーを追加して、git を拡張できます。この分野での良い例は、Android リポジトリです。

詳しく調べてみる価値のある他のビルドツール:

結論と参考文献

ビルド インフラストラクチャ (および Maven) のトピックに関するさらなる読み物が、Charles O'Farrell によって提案されました。これらは、非常に興味深い内容です。

私は上記の最後の記事からのこの素晴らしい引用で、この投稿を締めくくりたいと思います。これは Maven に関するものですが、他のビルド ツールや依存関係ツールにも同様に当てはまります。

キャッシュは処理速度の向上以外に何の働きもしません。キャッシュをすべて削除しても、周囲のシステムは以前同様に動作し、速度が遅くなるだけです。キャッシュには副作用もありません。これまでにキャッシュを使ってどんなことを実行してきたとしても、キャッシュに対してあるクエリを実行した結果と、今後同じクエリを行った結果はまったく同じ値になります。

Maven のエクスペリエンスは、私が説明したものとは大きく異なります。Maven リポジトリはキャッシュと同じように使用されますが、キャッシュのプロパティがありません。Maven リポジトリから何かを要求する場合、過去に何を行ったかが非常に重要です。直近に格納した内容が返されます。格納していない内容を要求すると失敗する場合があります。

このドラフトに思慮深いフィードバックを提供してくださった Charles O'Farrell に感謝します。上記のとおり、反映させていただきました。この他にも Git に関する誤りがありましたら、@durdn@AtlDevtools までお知らせください。

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

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

今すぐ始める