Articles

Gitサブモジュールのマスタリング

サブモジュール、ステップバイステップ

ここでは、コラボレーションプロジェクトでサブモジュールを使用するすべてのステップを探り、デフォルトの動作、トラップ、利用可能な改善を強調表示します。

あなたのフォローを容易にするために、私は彼らの”リモート”(実際には単なるディレクトリ)といくつかの例のレポをまとめました。,

  • mainはコンテナリポジトリとして機能し、最初のコラボレーターにローカルで、
  • プラグインはモジュールの中央メンテナンスリポジトリとして機能し、
  • リモートには前の二つのファイルシステムリモートが含まれています。レポス

以下のコマンド例では、プロンプトは常にどのリポジトリにいるかを表示します。,

サブモジュールの追加

まず、コンテナ内のサブモジュールとしてプラグインを追加しましょう(mainにあります)。 プラグイン自体は単純な構造を持っています:

.
├── README.md
├── lib
│ └── index.js
└── plugin-config.json

それでは、mainに入ってgit submodule addコマンドを使用しましょう。 これは、リモートのURLとサブモジュールを”インスタンス化”するサブディレクトリを取ります。

ここではリモートのUrlの代わりにパスを使用するため、よく知られていますが、奇妙なsnagがヒットしました:リモートの相対パスは、メインのリモートに対して解釈され、リポジトリのルートディレクトリに対しては解釈されません。, これは超奇妙で、どこにも記載されていませんが、私はそれが毎回起こるのを見ました。 だから言うのではなく。./remotes/プラグイン、我々はちょうど言います。./プラグイン。

main (master u=) $ git submodule add ../plugin vendor/plugins/demo
Cloning into 'vendor/plugins/demo'…
done.
main (master + u=) $

これは私たちのローカル設定にいくつかの設定を追加しました:

main (master + u=) $ cat .git/config


url = ../remotes/plugin

これはまた、二つのファイルを上演しました:

ハァッ?! なんだこれ。gitmodulesファイル? それを見てみましょう:

main (master + u=) $ cat .gitmodules

path = vendor/plugins/demo
url = ../plugin

これは私たちのローカル設定に酷似しています…なぜ重複するのですか? まあ、私たちのローカル設定が…ローカルだからです。, 私たちのコラボレーターはそれを見ることはできません(これは完全に正常です)ので、彼らは自分のレポで設定する必要があるすべてのサブモジュール これは何ですか。これは後でgit submodule initコマンドによって読み込まれます。

私たちがステータスにいる間、それが私たちのサブモジュールに来るとき、それがどのように最小限であるかに注意してください:それはちょうどその中, 私たちのサブモジュールは実際にサブディレクトリに挿入されました:


└── vendor
└── plugins
└── demo
├── .git
├── README.md
├── lib
│ └── index.js
└── plugin-config.json

logsやdiffのようなステータスは、ネストされたreposであるサブモジュールではなく、アクティブなリポジトリ(現在はコンテナ)に制限されています。 これはしばしば問題になります(このビューに限定されている場合、回帰を見逃すのは非常に簡単です)ので、サブモジュール認識のステータスを一度に設定することをお勧めします。

git config --global status.submoduleSummary true

そして今:

Aaaah、これは非常に優れています。, ステータスは、ベース情報を拡張して、vendor/plugins/demoに存在するサブモジュールが3つのニュースコミットを持っていることを追加します(作成したばかりのように、リモートブランチには三つのコミットしかなかったことを意味します)。最後のものは、”Fix repo name…”という最初のコミットメッセージ行を持つ追加(右向きの山かっこ>に注意してください)。

本当に私たちはここで二つの別々のレポを扱うことを家に持って来るために、のは、サブモジュールのディレクトリに入ることができます:

アクティブ,gitは引き継ぎます:カレントディレクトリ(デモ、サブモジュールのディレクトリ)で、a。gitは実際には単一のファイルであり、ディレクトリではありません。 内部を見てみましょう:

demo (master u=) $ cat .git
gitdir: ../../../.git/modules/vendor/plugins/demo

再び、Git1.7.8以降、Gitはコンテナの作業ディレクトリ内にrepoディレクトリを残すのではなく、これらをコンテナgitディレクトリ(内部。git/modules)、およびサブモジュールでgitdir参照を使用します。,

この背後にある理論的根拠は簡単です:これは、作業ディレクトリからサブモジュールのレポをスクラップし、後でそれを復元することなく、コンテナリポジトリがサブモジュールなしのブランチを持つことを可能にします。もちろん、サブモジュールを追加するときは、-b CLIオプションを使用して、特定のブランチ、または特定のコミットを使用することを選択できます(通常、デフォ これは、Gitが特定のSHA1ではなくmasterをチェックアウトしたためです。 Get-goから切り離されたヘッドを取得するには、SHA1を-bに指定する必要がありました。,

コンテナリポジトリに戻り、サブモジュールの追加を完了してリモートにプッシュしましょう。

demo (master u=) $ cd -
main (master + u=) $ git commit -m "Ajout submodule plugin demo"
main (master u+1) $ git push

サブモジュールを使用するリポジトリをつかむ

サブモジュールを使用するリポジトリのコラボレーションに関する問題を説明するために、パーソナリティを分割し、コンテナのリモートをクローンして作業を開始する同僚として行動します。 それを同僚ディレクトリに複製するので、いつでもどの人格キャップを持っているかをすぐに知ることができます。,そのベースディレクトリのみがここにあります:

vendor
└── plugins
└── demo

それはどのように起こりましたか? これは単に、これまでのところ、私たちの新しいレポ(同僚)がまだ私たちのサブモジュールを認識していないという事実によるものです:それの情報はローカルあなたが私を信じていない場合はgit/config)。 何に基づいてそれを埋める必要があります。gitmodulesは言わなければならない、これはまさにgit submodule initが行うことです:

Our。git/configは現在、サブモジュールを認識しています。, ただし、作業ディレクトリに存在することは言うまでもありませんが、リモートからフェッチしたことはありません。 しかし、当社の状況を示した。

参照してください、関連するコミットを手動で取得する必要があります。 それは私たちの最初のクローンがしたことではない、我々はすべてのプルでそれを行う必要が これは、適切に呼び出されたときに実際に自動化できる動作のクローンであるため、すぐにそれに戻ります。,

実際には、サブモジュールを使用してreposを扱うとき、通常は二つのコマンド(initとupdate)を一つにグループ化します。

colleague (master u=) $ git submodule update --init

Gitがあなた自身ですべてを より大きなFLOSSプロジェクトでは、サブモジュールが独自のサブモジュールを持っているときなど、これはすぐに悪夢になると想像してください。

GitはCLONEのためのCLIオプションを提供していますclone直後に再帰的に自動的にgit submodule update—init:かなり適切な名前の再帰オプション。,

それでは、もう一度全部を試してみましょう:

今それは良いです! 私たちは今、サブモジュール内の切り離された頭の上にいることに注意してください(私たちは今からですように):

git-subs $ cd colleague/vendor/plugins/demo
demo ((master)) $

私のプロンプトではなく、単一のセットで、括弧の二重のセットを参照してくださいかっこのセットを参照してくださいかっこのセット?, あなたのプロンプトが私のように設定されていない場合は、git_ps1_describe_style=branch環境変数を定義する必要があります)、次のようなものが表示されます。

demo ((fe64799...)) $

いずれにしても、ステータスはどこにいるかを確認します。

demo ((master)) $ git status
HEAD detached at fe64799
nothing to commit, working directory clean

サブモジュールのリモートから更新を取得します。

demo ((master)) $ git status
HEAD detached at fe64799
nothing to commit, working directory clean

サブモジュールのリモートから更新を取得します。/h2>

ok、私たち自身のrepo(Main)と”同僚の”(同僚)がすべて共同作業するように設定されたので、プラグインを維持する第三者の靴に足を踏み入れましょう。, ここでは、それに移動しましょう:

さて、二つの擬似コミットを追加し、これらをリモートに公開しましょう:

最後に、再び”最初の開発者”のキャップを置いてみましょう:

plugin (master u=) $ cd ../main
main (master u=) $

これら二つのコミットをサブモジュール内に取得したいとします。 これを実現するには、ローカルレポを更新し、作業ディレクトリに移動してアクティブなレポになるようにする必要があります。補足として、この種の更新にpullを使用することはお勧めしません。, 作業ディレクトリの更新を適切に取得するには、このコマンドでは、通常はそうではない適切なアクティブなブランチにいる必要があります(ほとんどの場合切り離された頭の上にいます)。 そのブランチのチェックアウトから始める必要があります。 しかし、もっと重要なのは、リモートブランチは、あなたが設定したいコミット以来、さらに先に進むことができ、プルはあなたのローカルコードベースに望ま,最初にgit fetchローカルキャッシュのリモートからすべての新しいデータを取得し、ログに記録して持っているものを確認し、目的のSHA1をチェックアウトし このアプローチには、より細かい制御に加えて、現在の状態(アクティブブランチまたはデタッチヘッド)に関係なく機能するという利点があります。

OK、だから私たちは良いです、無関係なコミットはありません。, それが可能であれば、私たちが興味を持っているものを明示的に設定しましょう(明らかにあなたは別のSHA1を持っています):

demo (master u-2) $ git checkout -q 0e90143

(-qは、私たちがどのように分離された頭に終わっているかについて私たちを惜しまないことだけがあります。 通常、この健全な通知が、ここにどんどん大きくなっていますましています。)

サブモジュールが更新されたので、コンテナリポジトリのステータスで結果を見ることができます。

ステータスの”古典的な”部分では、参照されるコミットが変更されたことを意味する新しいコミット変更タイプが表示されます。, もう一つの可能性(これに複合される可能性があります)は、新しいコンテンツです。

下部は、私たちのステータスによって有効になります。submoduleSummary=true以前の設定では、サブモジュールに触れた最後のコンテナコミット以来、導入されたコミットを明示的に述べています(右向き山かっこ>

“ひどいデフォルトの動作”ファミリでは、git diffは多くのことを望んでいます:

何-?, もっと便利なものを見ることができるCLIオプションがあります:

main (master * u=) $ git diff --submodule=log
Submodule vendor/plugins/demo fe64799..0e90143:
> Pseudo-commit #2
> Pseudo-commit #1

サブモジュールの参照コミット以外に、今のところ他のローカル変更はありません…これは強化されたgitステータス表示の下部とほぼ正確に一致していることに注意してください。

毎回そのようなCLIオプションを入力する必要があります(ちなみに、Gitの現在の補完オファーには表示されません)はむしろ扱いにくいです。 幸いなことに、一致する構成設定があります:

サブモジュールの更新を終了するコンテナコミットを実行するだけで済みます。, この更新で動作させるためにコンテナのコードに触れなければならない場合は、自然にコミットしてください。 一方、サブモジュール関連の変更やコンテナコードに関連する他のものを混在させることは避けてください:二つをきちんと分離することにより、後で他の

同僚のレポでこのサブモジュールの更新を取得しようとしているので、コミット直後にプッシュします(これは一般的な良い習慣ではありません)。

main (master * u=) $ git commit -am "Setting submodule on PC2"
main (master u+1) $ git push

サブモジュールを引っ張る-repoを使用して

クリック!, “同僚”キャップオン!

だから私たちはコンテナリポジトリのリモートから更新を引っ張っています…

(あなたは”正常にリベースされ、更新された…”を持っていないかもしれず、代わりに”‘再帰’戦略によって行われたマージ”を見るかもしれません。 その場合、心よりご要望がない限り、直ちについを引きrebase).

この表示の後半に注意してください:それは”Fetching submodule…”から始まるサブモジュールについてです。

この動作はGit1.7.5ではデフォルトになり、設定はfetchになりました。,recurseSubmodulesはデフォルトでon-demandになりました:コンテナプロジェクトが参照されるサブモジュールのコミットに対する更新を取得すると、これらのサブモジュール (フェッチはプルの最初の部分です。それでも、これは重要です:Gitは自動フェッチしますが、自動更新はしません。 ローカルキャッシュはサブモジュールのリモートで最新ですが、サブモジュールの作業ディレクトリは以前の内容に固執しています。 少なくとも、シャットとノートパソコン、ホップへの飛行機の中で、いちばしています。, この自動フェッチは既に知られているサブモジュールに限定されますが、ローカル設定にまだコピーされていない新しいサブモジュールは自動フェッチされません。

Gitは自動フェッチしますが、自動更新はしません。

現在のプロンプトは、アスタリスク(*)を持ち、WDはインデックスと同期しておらず、後者は新しく参照されるサブモジュールのコミットを認識しているため、ローカルな変更を示唆しています。 ステータスをチェックしてください:

山括弧がどのように左を指しているかに注意してください(<)?, Gitは、現在のWDはこの二つの約束に反して、コンテナプロジェクトの期待に応えます。

これは大きな危険です:サブモジュールの作業ディレクトリを明示的に更新しないと、次のコンテナコミットはサブモジュールを退行します。 これは一次トラップです。したがって、更新を完了することは必須です。

一般的な良い習慣を形成しようとしている限り、ここで推奨されるコマンドはgitサブモジュールupdate—init—recursive、新しいサブモジュールを自動初期化し、必要に応じてこれらを再帰的に更新するためです。,

別のエッジケースがあります:サブモジュールのリモートURLが最後に使用されてから変更された場合(おそらく協力者の一人がそれを変更しました。gitmodules)、これに一致するようにローカル設定を手動で更新する必要があります。 このような状況では、gitサブモジュールの更新の前に、gitサブモジュールsyncを実行する必要があります。

完全性のために、git submodule updateがデフォルトで参照されているSHA1をチェックアウトする場合でも、それを変更することができます(たとえば、ローカルサブモジュールの作業をリベースします(これについてはすぐに説明します)。, これを行うには、サブモジュールの更新構成設定をコンテナのローカル構成内でリベースするように設定します。そして、私は申し訳ありませんが、いいえ、プルで自動更新できるローカル構成設定、またはそのことについてはCLIオプションさえありません。 自動化ないもんを使用する必要はエイリアスカスタムスクリプト、丁寧に作り上げた地域。, Spullエイリアスの例を次に示します(単一行、表示用にここで分割)。

git config --global alias.spull '!git pull && git submodule sync --recursive && git submodule update --init --recursive'

git pullにカスタム引数を渡す機能を保持したい場合は、オンザフライで関数を定義して呼び出すか、カスタムスクリプトで行くことができます。 最初のアプローチは次のようになります(再び、単一行):

あまり読みやすくないでしょうか? うにカスタムスクリプトです。, Pathのどこかにgit-spullスクリプトファイルを置くとしましょう(私はそのようなもののために私のPATHに~/perso/binディレクトリを持っています):

#! /bin/bash
git pull "$@" &&
git submodule sync --recursive &&
git submodule update --init --recursive

実行権限を与えます:

chmod +x git-spull

これで、エイリアスを使用したのと同じように使用できるようになりました。

コンテナ内のサブモジュールをインプレースで更新する

これは最も難しいユースケースであり、できるだけそれから離れて、中央の専用レポを通じてメンテナンスを優先する必要があります。,

ただし、サブモジュールコードをコンテナコードの外でテストしたり、コンパイルしたりすることはできません。 多くのテーマやプラグイ

最初に理解すべきことは、コミットを行うため、適切な基礎から始めなければならないことです。 するためには検証することにより、支店の最新の犯ん”break”容器。 そうであれば、サブモジュールに独自のコンテナ固有のブランチを作成することは魅力的に聞こえますが、そのパスはサブモジュールとコンテナの間の強いカップリングにつながります。, また”submoduling”のコードでこのプロジェクトは、組み込みのように他の正内容です。

サブモジュールの現在のマスターブランチに良心をもって追加できることを認めましょう。 まず、リモートのローカル状態を同期することから始めましょう。

これについて別の方法は、コンテナリポジトリから、サブモジュールのローカルブランチをその追跡されたリモートブランチに明示的に同期することです(上に単一行、最後に空白が続きます)。

コードを編集し、動作させ、テストすることができます。, すべての設定が完了したら、二つのコミットと二つの必要なプッシュを実行できます(それは非常に簡単で、実際にはあまりにも頻繁に、そのいくつか

偽の作業を追加し、サブモジュールとコンテナレベルで関連する二つのコミットを行いましょう。

この時点で、大きな危険はサブモジュールをプッシュするのを忘れていることです。 コンテナプロジェクトに戻り、コミットして、コンテナをプッシュするだけです。 特にIDEやGUIの中では、簡単な間違いです。 同僚が更新を取得しようとすると、すべての地獄が緩んで壊れます。, 最初のステップを見てください:

Gitがサブモジュールのリモートから参照されるコミットを取得できなかったという兆候はまったくありません。 これの最初のヒントはステータスです:

警告に注意してください:明らかに、サブモジュールの新しく参照されたコミットはどこにも見つかりません。 実際、サブモジュールの作業ディレクトリを更新しようとすると、次のようになります。

main (master * u=) $ git submodule update
fatal: reference is not a tree: 12e3a529698c519b2fab790630f71bd531c45727
Unable to checkout '12e3a529698c519b2fab790630f71bd531c45727' in submodule path 'vendor/plugins/demo'

サブモジュールをプッシュすることを覚えておくことがどれほど重要であるか、理想的にはコンテナをプッシュする前にはっきりとわかります。, 私は現在参照されているサブモジュールのコミットもプッシュする必要があるかどうかを確認するCLIオプションがあることに注意してください。git push—recurse-submodules=on-demand( しかし、動作するようにプッシュするにはコンテナレベルのものが必要です。

さらに、(これには設定がないため、エイリアスの周りの手順を標準化する必要があります。spush:)—Git2.7.0から、pushがあります。,recurseSubmodules設定を定義できます(オンデマンドまたはチェックする)。

git config --global alias.spush 'push --recurse-submodules=on-demand'

サブモジュールの削除

サブモジュールを”削除”したい状況は二つあります。

  • 作業ディレクトリをクリアしたいだけです(おそらくコンテナのWDをアーカイブする前に)が、後で復元する可能性を保持したいと思います(したがって、それは残らなければなりません)。gitmodulesと.git/modules);
  • 現在のブランチから決定的に削除したいと思います。

それぞれのケースを順番に見てみましょう。,

一時的にサブモジュールを削除する

最初の状況はgit submodule deinitによって簡単に処理されます。 自分で見てください:

これはコンテナのステータスには何の影響もありません。 サブモジュールはいられるんでいます。git/config)ので、作業ディレクトリからの不在は気づかれません。 私たちはまだvendor/plugins/demoディレクトリを持っていますが、それは空です。

サブモジュールには、これを行うときにローカルに変更を加えてはいけません。,

gitサブモジュールの後のサブコマンドは、サブモジュールがローカル設定にないため、もう一度初期化するまで、このサブモジュールを幸せに無視します。 このようなコマ

一方、サブモジュールはで定義されたままです。gitmodules:initの後にupdate(または単一のupdate—init)が続くと、newとして復元されます。

サブモジュールを永久に削除する

これは、サブモジュールを取り除きたいことを意味します:通常のgit rmは、作業ディレクトリの他の部分と同じように行います。, これは、サブモジュールがgitfile(a.これはGit1.7.8から始まる場合です。 それ以外の場合は、手でこれを処理する必要があります(私は最後にどのように教えてあげましょう)。

作業ディレクトリからサブモジュールを取り除くことに加えて、コマンドは更新します。gitmodulesファイルは、サブモジュールを参照しなくなるようにします。 ここに行く:

当然のことながら、高度なステータス情報は、サブモジュールのgitfileがなくなっているため(実際には、デモディレクトリ全体が消えました)、ここ,しかし、奇妙なのは、ローカル設定がサブモジュール情報を保持していることです。 したがって、包括的な除去のために、適切にクリーンアップされるように、順番に両方を行うことをお勧めします(以前のコマンドの後にはクリアされgitmodules already):

git submodule deinit path/to/module # ensure local config cleanup
git rm path/to/module # clean WD and .gitmodules

あなたのアプローチにかかわらず、サブモジュールのリポジトリは存在したままです。git/modules/vendor/plugins/demo、しかし、あなたはいつでもそれを殺すのは自由です。

Git1.7より前にセットアップされたサブモジュールを削除する必要がある場合。,8、したがって、その埋め込みます。コンテナの作業ディレクトリ内のgitディレクトリ(gitfileに頼るのではなく)まっすぐに、ブルドーザーを壊す必要があります。rm-fr vendor/plugins/demoなど、前の二つのコマンドの前に手動でフォルダを削除する必要があります。