駅メモ!チームエンジニアの id:yumlonne です。
この記事ではスーパープロジェクト(サブモジュールが登録されている親プロジェクト)側で git checkout や git pull を実行したときに、自動で git submodule update 相当の処理を実行してくれる便利な設定を紹介します。
git submodule についてはドキュメントを参照してください。
記事中の各種動作は git version 2.38.1 で確認しています。
背景
私は最近サブモジュールが存在するプロジェクトを触り始めました。 しかし、git 操作をするときにサブモジュールが存在することを意識していないと、サブモジュールの参照を意図せず書き換えてしまうことがありました。
$ cd MyProject $ git checkout topic-A # サブモジュールをtopic-Aブランチが持っているコミットに向ける $ git submodule update $ git checkout topic-B # ここで git submodule update を忘れると、サブモジュールはtopic-Aの状態から更新されない! $ git add . # 気づかずにコミットすると、topic-Bのサブモジュールの向き先がtopic-Aと同じになってしまう $ git commit -m "hoge"
もちろんコミット前の確認やコードレビューがあるので気がつくことはできますが、pull や checkout をするたびに submodule update を打つのも面倒です。
そこで git config を調べたところ、submodule.recurse
というフラグで実現できそうということが分かりました。
このフラグは様々な git コマンドの--recurse-submodules
オプションを制御するため、他のよく使いそうな git コマンドに与える影響も調べてみました。
各コマンドへの影響
以下の各コマンドはgit config --global submodule.recurse true
を設定した上で検証しています。
詳細な影響は man git config
やman git ${command}
で--recurse-submodules オプションの説明を参照してください。
switch
ブランチを切り替えたときにサブモジュールも自動で追従します。
$ git submodule status 5b8930e2a251ead82076cf17cab13b95f0ec392d SubProject (5b8930e) $ git switch topic-A Switched to branch 'topic-A' $ git submodule status 89c87486bd15a4ebc84a7166a46977806ecfaced SubProject (heads/main-1-g89c8748)
restore
サブモジュールのファイルも復元されるようになります。
$ ls README.md SubProject $ ls SubProject/ README.md $ echo "hoge" >> README.md $ echo "fuga" >> SubProject/README.md $ git status On branch main Changes not staged for commit: (use "git add <file>..." to update what will be committed) (use "git restore <file>..." to discard changes in working directory) (commit or discard the untracked or modified content in submodules) modified: README.md modified: SubProject (modified content) $ git restore . $ git status On branch main nothing to commit, working tree clean
checkout
checkout でブランチを切り替えた場合は switch 相当、ファイルを復元した場合は restore 相当の動作になります。
pull
サブモジュールの新しいコミットも取得し、必要があれば switch と同様に自動で追従してくれます。
$ git submodule status 61b0cf596df0b2c68617be4a31f0feeca270d3f1 SubProject (61b0cf5) $ git pull origin main From github.com:yumlonne/MyProject * branch main -> FETCH_HEAD Fetching submodule SubProject Updating ae9b6ab..edcc372 Fast-forward SubProject | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) Successfully rebased and updated detached HEAD. Submodule path 'SubProject': rebased into '5b8930e2a251ead82076cf17cab13b95f0ec392d' $ git submodule status 5b8930e2a251ead82076cf17cab13b95f0ec392d SubProject (5b8930e)
submodule.recurse true
の設定により、 pull のたびにサブモジュールのフェッチが実行されます。
以下のように fetch の config としてon-demand
を指定することで、変更されたサブモジュールのみフェッチされるようになります。
git config --global fetch.recurseSubmodules on-demand
push
スーパープロジェクトで push を実行したとき、サブモジュール側のコミットも一緒に push してくれます。
$ cd SubProject/ $ echo "hoge" >> README.md $ git add . $ git commit -m "update README.md" $ cd ../ $ git add . $ git push origin main Pushing submodule 'SubProject' Enumerating objects: 5, done. Counting objects: 100% (5/5), done. Delta compression using up to 2 threads Compressing objects: 100% (2/2), done. Writing objects: 100% (3/3), 322 bytes | 322.00 KiB/s, done. Total 3 (delta 0), reused 0 (delta 0), pack-reused 0 To github.com:yumlonne/SubProject.git 47cd1b1..be119ef main -> main Enumerating objects: 3, done. Counting objects: 100% (3/3), done. Delta compression using up to 2 threads Compressing objects: 100% (2/2), done. Writing objects: 100% (2/2), 237 bytes | 237.00 KiB/s, done. Total 2 (delta 1), reused 0 (delta 0), pack-reused 0 remote: Resolving deltas: 100% (1/1), completed with 1 local object. To github.com:yumlonne/MyProject.git 43ddc9d..9030778 main -> main
他にも、config でpush.recurseSubmodules
を設定するか、push 時にオプションを渡すことで挙動をカスタマイズできます。(長くなるので省略します)
grep
サブモジュールのファイルも grep できるようになります。
$ git grep -n hoge SubProject/README.md:2:hoge
まとめ
submodule.recurse true
を設定することで、色々なコマンドがサブモジュールを意識して動いてくれるようになりました。
ここでは紹介していないコマンドやオプションもあるので、使う際はお手元の git のバージョンに対応したドキュメントを読むことをおすすめします。