git branch のイラスト

Git サブツリー: Git サブモジュールの代替

Nicola Paolucci
Nicola Paolucci
リストに戻る

インターネットには、Git サブモジュールを使うべきではないという記事があふれています。若干の使用例でサブモジュールが役立つ場合があるものの、欠点はいくつもあります。

代替手段はあるでしょうか? もちろんあります。少なくとも 2 つのツールでは、Git を使い続けながら、プロジェクト内のソフトウェアの依存関係の履歴を追跡できます。

  • Git subtree
  • google repo

この記事では、git subtree に注目し、完全とまでは言えないもののそれが git submodule の問題を解決するものであることを説明してみます。

git subtree とは何ですか? なぜそれを使う必要があるのですか?

git subtree では、あるリポジトリをサブディレクトリとして別のリポジトリにネストできます。これは Git プロジェクトでプロジェクトの依存関係を管理するいくつかの方法のうちの 1 つです。

Git サブツリーの使用前後の 2 つのリポジトリ間の対話を示す図。

git subtree を検討する理由

  • シンプルなワークフローは管理が簡単です。
  • 古い (v1.5.2 より前の) バージョンの Git がサポートされています。
  • 親プロジェクトのクローンが完了した直後からサブプロジェクトのコードが利用可能です。
  • git subtree では、リポジトリのユーザーが新たに学ばなければならないことはありません。git subtree を使用していることを関知せず、依存関係を管理することができます。
  • git subtree は git submodule のように新しいメタデータ ファイル (.gitmodule) を追加しません。
  • 依存関係にあるモジュールの内容を変更する場合でも、そのリポジトリのコピーを別に持つ必要がない。

欠点 (ただし、その大部分は許容可能と考えられます):

  • 利用者には、新たなマージ戦略 (つまり .git subtree) に関する知識が必要です。
  • サブプロジェクトの upstream にコードを戻す手順はやや複雑になります。
  • 親プロジェクト コードとサブプロジェクト コードをコミットに混在させない責任は各自が負います。

git subtree の使用方法

git subtree は 2012 年 5 月以降にリリースされた (Git の v1.7.11 以降) で利用可能です。OSX 上で homebrew を使用してインストールしたバージョンにはサブツリーも適切に組み込まれていますが、プラットフォームによってはインストール手順の実行が必要です。

典型的な使用例として、git subtree を利用して vim プラグインのトラッキングを行う場合を説明しましょう。

リモート追跡を使用しない迅速かつ不正な方法

数行のみカット アンド ペーストしたいだけであれば、この段落をお読みください。最初に、指定したプレフィックス フォルダーに git subtree を追加します。

 git subtree add --prefix .vim/bundle/tpope-vim-surroundhttps://bitbucket.org/vim-plugins-mirror/vim-surround.git main --squash

(通常、サブプロジェクトの履歴のすべてを親リポジトリに保存することはありませんが、保存したい場合は --squash フラグを削除すればよいでしょう)。

上記のコマンドで次の出力が生成されます。

git fetch https://bitbucket.org/vim-plugins-mirror/vim-surround.git main
warning: no common commits
remote: Counting objects: 338, done.
remote: Compressing objects: 100% (145/145), done.
remote: Total 338 (delta 101), reused 323 (delta 89)
Receiving objects: 100% (338/338), 71.46 KiB, done.
Resolving deltas: 100% (101/101), done.
From https://bitbucket.org/vim-plugins-mirror/vim-surround.git
* branch main -} FETCH_HEAD
Added dir '.vim/bundle/tpope-vim-surround'

ご覧のとおり、ここでは vim-surround リポジトリの全履歴を 1 つにスカッシュしたマージ コミットを記録しています。

1bda0bd [3 minutes ago] (HEAD, stree) Merge commit 'ca1f4da9f0b93346bba9a430c889a95f75dc0a83' as '.vim/bundle/tpope-vim-surround' [Nicola Paolucci]
ca1f4da [3 minutes ago] Squashed '.vim/bundle/tpope-vim-surround/' content from commit 02199ea [Nicola Paolucci]

後から upstream リポジトリからプラグインのコードをアップデートする場合は、単に git subtree pull を実行すればよいだけです。

git subtree pull --prefix .vim/bundle/tpope-vim-surround https://bitbucket.org/vim-plugins-mirror/vim-surround.git main --squash

この方法は簡便ですが、ややコマンドが長く覚えにくいのが欠点です。サブプロジェクトをリモートとして作成することにより、コマンドを短くすることができます。

サブプロジェクトをリモートとして追加する

サブツリーをリモートとして追加すると、さらに短い形式で参照できるようになります。

git remote add -f tpope-vim-surround https://bitbucket.org/vim-plugins-mirror/vim-surround.git

これで (以前と同様に) サブツリーを追加できますが、短い形式でリモートを参照できるようになりました。

git subtree add --prefix .vim/bundle/tpope-vim-surround tpope-vim-surround main --squash

サブプロジェクトを後の日付でアップデートするコマンドは次のようになります。

git fetch tpope-vim-surround main
git subtree pull --prefix .vim/bundle/tpope-vim-surround tpope-vim-surround main --squash

アップストリームに戻す

これで、ローカルの作業ディレクトリにあるサブプロジェクトに修正を自由にコミットできます。ローカルの貢献をアップストリーム プロジェクトに戻す場合は、そのプロジェクトをフォークして別のリモートとして作成する必要があります。

git remote add durdn-vim-surround ssh://git@bitbucket.org/durdn/vim-surround.git

ここで、次のように subtree push コマンドを実行することができます。

git subtree push --prefix=.vim/bundle/tpope-vim-surround/ durdn-vim-surround main
git push using: durdn-vim-surround main
Counting objects: 5, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (3/3), done.
Writing objects: 100% (3/3), 308 bytes, done.
Total 3 (delta 2), reused 0 (delta 0)
To ssh://git@bitbucket.org/durdn/vim-surround.git
02199ea..dcacd4b dcacd4b21fe51c9b5824370b3b224c440b3470cb -} main

これが完了すると、パッケージ開発全体のメンテナーに対してプル リクエストを送る準備が整います。

git subtree コマンドを使わずにこの作業を行えますか?

はい、可能です。git subtree は、サブツリーのマージ戦略とは異なるものです。何らかの理由で git subtree が利用できない場合であっても、マージ戦略を利用することは可能です。ここではその手順を説明します。

依存関係を単純な git remote として追加します。

git remote add -f tpope-vim-surround https://bitbucket.org/vim-plugins-mirror/vim-surround.git

依存関係の内容をリポジトリに読み込む前に、この時点までのプラグインのツリー履歴全体を追跡できるように、マージを記録することが重要です。

git merge -s ours --no-commit tpope-vim-surround/main

これにより次のような出力が得られます:

Automatic merge went well; stopped before committing as requested

次に、最新のツリー オブジェクトの内容をプラグイン リポジトリに読み込み、コミット可能な作業ディレクトリに格納します。

git read-tree --prefix=.vim/bundle/tpope-vim-surround/ -u tpope-vim-surround/main

これでコミット可能になります (また、これが読み込んだツリーの履歴を保存するマージ コミットになります)。

git ci -m"[subtree] adding tpope-vim-surround"
[stree 779b094] [subtree] adding tpope-vim-surround

プロジェクトをアップデートする場合は、git subtree マージ戦略を使用して pull を実行すればよいでしょう。

git pull -s subtree tpope-vim-surround main

優れた代替手段である git subtree

git サブモジュールをしばらく使ってみると、git subtree によって git サブモジュールのさまざまな問題が解決することがわかります。いつものことですが、Git の機能はどれも活用するにはラーニング カーブに沿った経験が必要なのです。

Twitter で @durdn をフォローして、Git に関する他の情報も入手しましょう。Git リポジトリを管理する適切なツールをお探しの場合は、Atlassian Bitbucket をご覧ください。

アップデート: 私はこの記事を公開した後、Git subtree の機能に関する記事も執筆しました

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

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

今すぐ始める