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

インターネットには、Git サブモジュールを使うべきではないという記事があふれています。若干の事例でサブモジュールが役立つ場合があるものの、欠点はいくつもあります。
代替手段はあるでしょうか? もちろんあります。少なくとも 2 つのツールでは、Git を使い続けながら、プロジェクト内のソフトウェアの依存関係の履歴を追跡できます。
Git subtreegoogle repo
この記事では、Git サブモジュールの問題の一部を解決する git subtree について説明します。
git subtree とは何ですか?なぜそれを使う必要があるのですか?
git subtree では、あるリポジトリをサブディレクトリとして別のリポジトリにネストできます。これは Git プロジェクトでプロジェクトの依存関係を管理するいくつかの方法のうちの 1 つです。

git subtree を検討する理由
シンプルなワークフローは管理が簡単です。
古い (v1.5.2 より前の) バージョンの Git がサポートされています。
親プロジェクトのクローンが完了した直後からサブプロジェクトのコードが利用可能です。
git subtreeでは、リポジトリのユーザーが新たに学ばなければならないことはありません。git subtreeを使用していることを関知せず、依存関係を管理することができます。git subtreeは Git サブモジュールのように新しいメタデータ・ファイル(.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-surround https://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 つにスカッシュした merge commit を記録しています。
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アップストリームに戻す
これで、ローカルの作業ディレクトリにあるサブプロジェクトに修正を自由にコミットできます。ローカルのコントリビューションを upstream プロジェクトに戻す場合は、そのプロジェクトをフォークして別のリモートとして作成する必要があります。
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 の機能を活用するにはラーニング・カーブに沿った経験が必要なのです。
Git サブツリーの機能に関するこちらの記事をご覧ください。