Gitサブモジュールをオリジンの最新のコミットに更新する


853

Gitサブモジュールを含むプロジェクトがあります。これはssh:// ... URLからのものであり、コミットAにあります。コミットBはそのURLにプッシュされており、サブモジュールにコミットを取得して変更してもらいたいのです。

さて、私の理解ではgit submodule updateこれでうまくいくはずですが、そうではありません。何もしません(出力なし、成功終了コード)。次に例を示します。

$ mkdir foo
$ cd foo
$ git init .
Initialized empty Git repository in /.../foo/.git/
$ git submodule add ssh://user@host/git/mod mod
Cloning into mod...
user@host's password: hunter2
remote: Counting objects: 131, done.
remote: Compressing objects: 100% (115/115), done.
remote: Total 131 (delta 54), reused 0 (delta 0)
Receiving objects: 100% (131/131), 16.16 KiB, done.
Resolving deltas: 100% (54/54), done.
$ git commit -m "Hello world."
[master (root-commit) 565b235] Hello world.
 2 files changed, 4 insertions(+), 0 deletions(-)
 create mode 100644 .gitmodules
 create mode 160000 mod
# At this point, ssh://user@host/git/mod changes; submodule needs to change too.
$ git submodule init
Submodule 'mod' (ssh://user@host/git/mod) registered for path 'mod'
$ git submodule update
$ git submodule sync
Synchronizing submodule url for 'mod'
$ git submodule update
$ man git-submodule 
$ git submodule update --rebase
$ git submodule update
$ echo $?
0
$ git status
# On branch master
nothing to commit (working directory clean)
$ git submodule update mod
$ ...

私も試してみましgit fetch modたが、フェッチを実行しているように見えます(ただし、パスワードの入力を求められないため、実行できない可能性がgit logあります)が、git show新しいコミットの存在を拒否しています。これまでのところrm、モジュールを追加して再度追加しているだけですが、これは原則として間違っており、実際には面倒です。


5
デビッドZの答えはこれを行うためのより良い方法のようです-Gitは--remoteオプションを介して組み込みが必要な機能を持っているので、おそらくそれをJasonの答えの「手動」アプローチではなく受け入れられた答えとしてマークすることは有用でしょうか?
Mark Amery、2015

1
@MarkAmeryに強く同意します。Jasonは実用的なソリューションを提供しましたが、サブモジュールのコミットポインターを間違ったコミットIDに残してしまうため、それを行う意図された方法ではありません。--remote現時点では、新しい方が確実に優れたソリューションであり、この質問はサブモジュールに関するGithub Gistからリンクされているので、新しい読者には新しい答えを見てもらうほうが良いと思います。
MutantOctopus

hunter2パスワードを使った素敵なタッチ:o)
lfarroco

回答:


1458

このgit submodule updateコマンドは実際に、スーパーモジュールのインデックスですでに指定されているコミットをサブモジュールでそれぞれチェックアウトするようにGitに指示します。リモートから利用可能な最新のコミットにサブモジュールを更新したい場合は、サブモジュールで直接行う必要があります。

要約すると:

# Get the submodule initially
git submodule add ssh://bla submodule_dir
git submodule init

# Time passes, submodule upstream is updated
# and you now want to update

# Change to the submodule directory
cd submodule_dir

# Checkout desired branch
git checkout master

# Update
git pull

# Get back to your project root
cd ..

# Now the submodules are in the state you want, so
git commit -am "Pulled down update to submodule_dir"

または、忙しい人なら:

git submodule foreach git pull origin master

335
git submodule foreach git pull
Mathias Bynens、2011年

87
@Nicklasその場合は、を使用しますgit submodule foreach git pull origin master
Mathias Bynens、2011

54
この時点で、これらのすべての修正が修正されたので、誰かが説明のブログ投稿を書いて私をそこに向ける必要があります。お願いします。
Suz

25
「foreach」アプローチのマイナーな改善-サブモジュール内にサブモジュールがある場合は、そこに--recursiveを追加することができます。そう:git submodule foreach --recursive git pull origin master
orion elenzil 14

4
@Abdull 「変更および削除されたファイルを自動的にステージングするコマンドを伝えますが、Gitに通知していない新しいファイルは影響を受けません」の-aスイッチgit commit
godfrzero

473

Git 1.8.2には、--remoteこの動作を正確に有効にする新しいオプションが備わっています。ランニング

git submodule update --remote --merge

各サブモジュールのアップストリームから最新の変更をフェッチし、それらをマージして、サブモジュールの最新リビジョンをチェックアウトします。ドキュメントプットそれ:

-リモート

このオプションは、updateコマンドでのみ有効です。スーパープロジェクトの記録されたSHA-1を使用してサブモジュールを更新する代わりに、サブモジュールのリモート追跡ブランチのステータスを使用します。

これはgit pull、各サブモジュールで実行することと同じです。これは、通常、まさにあなたが望むものです。


4
git pull各サブモジュールで実行することと同等」明確にするために、あなたの答えとgit submodule foreach git pull?の間に(ユーザーの観点から)違いはありません。
Dennis

3
@Dennisそれは本質的に同じことをしますが、機能がまったく同じかどうかはわかりません。たとえば、2つのコマンドがいくつかの構成設定に応答する方法など、私が知らないいくつかの小さな違いがあるかもしれません。
David Z

5
この10,000Xを賛成できればいいのに。なぜこれがgitのドキュメントにどこにも表示されていないのですか?巨大な見落とし。
serraosays

4
私にとって、それらは実際にはかなり異なっていました。foreach git pullそれらをチェックアウトするだけで、サブモジュールの新しいコミットを指すようにメインリポジトリのポインターを更新しませんでした。それだけで--remote、最新のコミットを指すようになりました。
Ela782

5
--mergeオプションを使用する理由 それはどのような違いをもたらしますか?
mFeinstein 2017年

127

プロジェクトの親ディレクトリで、次を実行します。

git submodule update --init

または、再帰的なサブモジュールを実行している場合:

git submodule update --init --recursive

サブモジュールの更新中にローカルサブモジュールディレクトリに何らかの変更が加えられるため、これがまだ機能しない場合があります。

ほとんどの場合、ローカルの変更はコミットしたいものではない可能性があります。これは、サブモジュールでのファイルの削除などが原因で発生する可能性があります。その場合は、ローカルサブモジュールディレクトリとプロジェクトの親ディレクトリでリセットを実行し、再度実行します。

git submodule update --init --recursive

5
これが本当の答えです。どういうわけかそれを私のリモートリポジトリにプッシュできますか?
MonsterMMORPG 2016

これは新しいサブモジュールで機能します!他のすべてを更新できましたが、このコマンドを実行するまで、新しいサブモジュールのフォルダーは空のままです。
Alexis Wilke、

1
既存のサブモジュールの変更はプルされません
セルゲイG.

73

メインプロジェクトは、サブモジュールがあるべき特定のコミットを指します。git submodule update初期化された各サブモジュールでそのコミットをチェックアウトしようとします。サブモジュールは実際には独立したリポジトリです。サブモジュールで新しいコミットを作成し、それをプッシュするだけでは十分ではありません。また、メインプロジェクトにサブモジュールの新しいバージョンを明示的に追加する必要もあります。

したがって、あなたのケースでは、サブモジュールで正しいコミットを見つける必要があります-それがの先端だとしましょうmaster

cd mod
git checkout master
git pull origin master

次にメインプロジェクトに戻り、サブモジュールをステージングしてコミットします。

cd ..
git add mod
git commit -m "Updating the submodule 'mod' to the latest version"

次に、メインプロジェクトの新しいバージョンをプッシュします。

git push origin master

この時点から、他の誰かがメインプロジェクトgit submodule updateを更新すると、初期化されていると想定して、サブモジュールが更新されます。


24

この説明では、2つの異なるシナリオが混在しているようです。

シナリオ1

親リポジトリのサブモジュールへのポインターを使用して、おそらくすべてのサブモジュールを最初に繰り返し、リモートから更新/プルした後、親リポジトリが指している各サブモジュールのコミットをチェックアウトしたいと思います。

これは、指摘したように、

git submodule foreach git pull origin BRANCH
git submodule update

OPが目指しているシナリオ2

1つ以上のサブモジュールで新しいことが発生しました。1)これらの変更をプルし、2)親リポジトリを更新して、この/これらのサブモジュールのHEAD(最新)コミットを指すようにします。

これは

git submodule foreach git pull origin BRANCH
git add module_1_name
git add module_2_name
......
git add module_n_name
git push origin BRANCH

親リポジトリのコミットポインタを更新するスクリプトなどで、n個すべてのサブモジュールへのn個のパスをハードコーディングする必要があるため、あまり実用的ではありません。

サブモジュールの先頭を指すように(を使用してgit add)親リポジトリポインターを更新し、各サブモジュールを自動反復させると便利です。

このために、この小さなBashスクリプトを作成しました。

git-update-submodules.sh

#!/bin/bash

APP_PATH=$1
shift

if [ -z $APP_PATH ]; then
  echo "Missing 1st argument: should be path to folder of a git repo";
  exit 1;
fi

BRANCH=$1
shift

if [ -z $BRANCH ]; then
  echo "Missing 2nd argument (branch name)";
  exit 1;
fi

echo "Working in: $APP_PATH"
cd $APP_PATH

git checkout $BRANCH && git pull --ff origin $BRANCH

git submodule sync
git submodule init
git submodule update
git submodule foreach "(git checkout $BRANCH && git pull --ff origin $BRANCH && git push origin $BRANCH) || true"

for i in $(git submodule foreach --quiet 'echo $path')
do
  echo "Adding $i to root repo"
  git add "$i"
done

git commit -m "Updated $BRANCH branch of deployment repo to point to latest head of submodules"
git push origin $BRANCH

実行するには、

git-update-submodules.sh /path/to/base/repo BRANCH_NAME

精巧

まず、$ BRANCH(2番目の引数)という名前のブランチがすべてのリポジトリに存在するとします。これをさらに複雑にしてもかまいません。

最初の2つのセクションでは、引数があるかどうかを確認しています。次に、親リポジトリの最新のものをプルします(プルを実行しているときはいつでも--ff(早送り)を使用することを好みます。リベースしました、BTW)。

git checkout $BRANCH && git pull --ff origin $BRANCH

次に、新しいサブモジュールが追加されたか、まだ初期化されていない場合、いくつかのサブモジュールの初期化が必要になることがあります。

git submodule sync
git submodule init
git submodule update

次に、すべてのサブモジュールを更新/プルします。

git submodule foreach "(git checkout $BRANCH && git pull --ff origin $BRANCH && git push origin $BRANCH) || true"

いくつかのことに注意してください:まず、私はいくつかのGitコマンドを使用してチェーンしています&&-つまり、前のコマンドがエラーなしで実行される必要があるということです。

プルが成功した後(リモートで新しいものが見つかった場合)、プッシュを実行して、マージコミットの可能性がクライアントに残されないようにします。繰り返しますが、プルが実際に新しいものをもたらした場合にのみ発生します。

最後に、|| trueエラーが発生してもスクリプトが続行されるようにすることが最後です。これを機能させるには、反復のすべてを二重引用符で囲み、Gitコマンドを括弧で囲む必要があります(演算子の優先順位)。

私のお気に入りの部分:

for i in $(git submodule foreach --quiet 'echo $path')
do
  echo "Adding $i to root repo"
  git add "$i"
done

すべてのサブモジュールを反復処理します-を--quiet使用すると、「Entering MODULE_PATH」出力が削除されます。'echo $path'(単一引用符で囲む必要があります)を使用すると、サブモジュールへのパスが出力に書き込まれます。

この相対サブモジュールパスのリストは、配列($(...))にキャプチャされます-最後にこれを繰り返しgit add $i、親リポジトリを更新します。

最後に、親リポジトリが更新されたことを説明するメッセージを含むコミット。何も行われなかった場合、このコミットはデフォルトで無視されます。これをオリジンにプッシュすれば完了です。

Jenkinsジョブでこれを実行するスクリプトがあり、後でスケジュールされた自動デプロイメントにチェーンします。これは魅力的なように機能します。

これが誰かのお役に立てば幸いです。


2
!@#$%SO私たちはあなたのようなスクリプトを使用しています。注意: `` `git submodule foreach --quiet 'echo $ path'` ``の代わりに、forループ内で `` `git submodule foreach --recursive --quiet pwd` ``を使用します。このpwdコマンドは、存在する各サブモジュールの適切な「絶対パス」を出力します。大規模なプロジェクトに存在する可能性があるsubmodules-within-submodules -...を含むすべてのサブモジュールに--recursive確実にアクセスします。どちらの方法でも、スペースを含むディレクトリで問題が発生します。たとえば/c/Users/Ger/Project\ Files/...、ポリシーでは、プロジェクトのどこにも空白を使用しないようにします。
Ger Hobbelt 14

2
これはいいことであり、質問が何であるかについてのいくつかの回答に誤解があることは正しいですが、David Zの優れた回答で指摘されているように、2013年中頃から機能がGitに組み込まれているため、スクリプトは不要です。彼らは--remoteオプションを追加しました。git submodule update --remoteスクリプトとほぼ同じように動作します。
Mark Amery、2015

@GerHobbeltありがとう。正解です。サブモジュールは1レベルしかないため、再帰的にするつもりはありません。スクリプトが期待どおりに機能することを確認する機会が得られる前に、スクリプトを更新しませんが、間違いなく私のスクリプトはサブサブモジュールを1つにまとめます。フォルダ内のスペースに関しては、これは間違いなく避けるべきもののように思えます!:S
FrederikStruck-Schøning

@MarkAmeryご意見ありがとうございます。ただし、1つの問題が発生します。引数ではなく、サブモジュールのブランチを指定できません。gitマニュアルより:The remote branch used defaults to master, but the branch name may be overridden by setting the submodule.<name>.branch option in either .gitmodules or .git/config (with .git/config taking precedence).マスター以外のブランチに対してこれを行うたびに.gitmodulesも.git / configも編集したくありません。しかし、私は何かを逃したのでしょうか?また、このメソッドは再帰的なマージを強制するようです(したがって、早送りの可能性がありません)。
FrederikStruck-Schøning、2015

最後に、@ DavidZの方法を試してみたところ、正確には実行されないようで、実行しようとしました(どの操作について問い合わせていましたか):サブモジュールのHEADコミットを親に追加します(つまり、「ポインタの更新」 )。ただし、すべてのサブモジュールで最新の変更をフェッチしてマージするという非常に優れた(そしてより速い)唯一の仕事をしているようです。悲しいかな、デフォルトではマスターブランチからのみ(.gitmodulesファイルを編集しない限り(上記を参照))。
FrederikStruck-Schøning、2015

19

プレーンでシンプル、サブモジュールを取得するには:

git submodule update --init --recursive

そして、それらを最新のマスターブランチに更新します(たとえば)。

git submodule foreach git pull origin master

12

ただし、サブモジュールのコミットを更新する最新の形式は次のとおりです。

git submodule update --recursive --remote --merge --force

古い形式は次のとおりです。

git submodule foreach --quiet git pull --quiet origin

ただし、この2番目の形式は実際には「静か」ではありません。

参照してくださいa282f5aコミットにより(2019年4月12日)のグエンタイ・ゴックDuyと(pclouds
(合併によりJunio C浜野- gitster-コミットf1c9f6c、2019年4月25日)

submodule foreach:「<command> --quiet」が尊重されていないのを修正

ロビンはそれを報告しました

git submodule foreach --quiet git pull --quiet origin

もう本当に静かではありません。
前にそれは静かにする必要がありfc1b924submodule:ポートのsubmoduleサブコマンド「foreachC、2018年5月10日までシェルから」、Gitのv2.19.0-RC0)ので、parseopt誤って[オプション]を食べることができません。

" git pull"は--quiet与えられていないかのように動作します。

これは、parseoptin submodule--helperが両方の--quietオプションをforeachのオプションであるかのように解析しようとするために発生しますgit-pull
解析されたオプションは、コマンドラインから削除されます。後でプルするときは、これだけを実行します

git pull origin

サブモジュールヘルパーを呼び出すときに、「--」の前に「git pull」をparseopt追加すると、実際には属していない解析オプションで停止します submodule--helper foreach

PARSE_OPT_KEEP_UNKNOWN安全対策として取り外しました。parseopt不明なオプションが表示されることはありません。私がそれらを見ている間、いくつかの使用法の文字列の更新もあります。

その間、「--」を渡す他のサブコマンドに「」も追加$@submodule--helperます。$@これらの場合の「」はパスであり、そうなる可能性は低くなります --something-like-this
しかし、要点はまだ残っており、git-submoduleオプションとは何か、パスとは何かを解析および分類しています。
submodule--helpergit-submoduleように見えても、渡されたパスをオプションと見なすべきではありません。


また、Git 2.23(2019年第3四半期)では、別の問題が修正されています。git submodule foreach--recursive」オプションが使用されている場合、「」は、各サブモジュールで正しく実行されるコマンドに渡されるコマンドラインオプションを保護しませんでした。

Morian Sonnet(momoson)によるcommit 30db18b(2019年6月24日)を参照してください。
(合併によりJunio C浜野- gitster-コミット968eecb、2019年7月9日)

submodule foreach:オプションの再帰を修正

呼び出し:

git submodule foreach --recursive <subcommand> --<option>

オプション--<option>が不明である ことを示すエラーが発生しますsubmodule--helper
もちろん、これはがの<option>有効なオプションではない場合に限られgit submodule foreachます。

これは、上記の呼び出しが内部的にsubmodule--helperの呼び出しに変換されるためです。

git submodule--helper foreach --recursive \
    -- <subcommand> --<option>

この呼び出しは、最初のレベルのサブモジュール内でオプションを指定してサブコマンドを実行することから始まり、呼び出しの次の反復を呼び出すことによって続きsubmodule foreachます

git --super-prefix <submodulepath> submodule--helper \
   foreach --recursive <subcommand> --<option>

最初のレベルのサブモジュール内。サブコマンドの前にある2つのダッシュがないことに注意してください。

PARSE_OPT_KEEP_UNKNOWNの引数解析のフラグgit submodule foreachがコミットa282f5aで削除されたため、この問題はごく最近発生し始めました。
したがって、引数の解析が二重ダッシュで適切に終了していないため、今のところ不明なオプションに不満があります。

このコミットは、再帰中にサブコマンドの前に二重ダッシュを追加することで問題を修正します。


7
git pull --recurse-submodules

これにより、最新のコミットがすべてプルされます。


4

私の場合、git最新のものに更新すると同時に、不足しているファイルを再入力したいと考えていました。

以下は、不足しているファイルを復元しました(--forceここでは言及されていないようです)。ただし、新しいコミットは取得されませんでした。

git submodule update --init --recursive --force

これはしました:

git submodule update --recursive --remote --merge --force


3

@Jasonはある意味では正しいですが、完全ではありません。

更新

登録されているサブモジュールを更新します。つまり、欠落しているサブモジュールを複製し、含まれているリポジトリのインデックスで指定されているコミットをチェックアウトします。--rebaseまたは--mergeが指定されていないか、サブモジュールのキー$ module.updateがrebaseまたはmergeに設定されていない限り、これによりサブモジュールHEADがデタッチされます。

したがって、git submodule updateチェックアウトは行いますが、それは包含リポジトリーのインデックスでのコミットに対するものです。それはまだアップストリームの新しいコミットをまだ知りません。したがって、サブモジュールに移動して、必要なコミットを取得し、メインリポジトリで更新されたサブモジュールの状態をコミットしてから、を実行しgit submodule updateます。


1
サブモジュールを別のコミットに移動してからを実行git submodule updateすると、サブモジュールがスーパープロジェクトの現在のHEADで指定されているコミットに移動するようです。(スーパープロジェクトの最新のコミットがサブプロジェクトのあるべきだと言っているものは何でも—この動作は、ジェイソンの投稿の説明の後、私には論理的に思えます)フェッチするようにも見えますが、サブプロジェクトが間違ったコミットにある場合のみ、それは私の混乱を増していました。
タナトス

2

すべてをマスターの最新のものに更新するための素晴らしいワンライナーは次のとおりです。

git submodule foreach 'git fetch origin --tags; git checkout master; git pull' && git pull && git submodule update --init --recursive

Mark Jaquithに感謝


2

ホストブランチがわからない場合は、次のようにします。

git submodule foreach git pull origin $(git rev-parse --abbrev-ref HEAD)

メインのGitリポジトリのブランチを取得し、サブモジュールごとに同じブランチをプルします。


0

master各サブモジュールのブランチをチェックアウトする場合は、その目的で次のコマンドを使用できます。

git submodule foreach git checkout master
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.