Mobile Factory Tech Blog

技術好きな方へ!モバイルファクトリーのエンジニアたちが楽しい技術話をお届けします!

git submodule update 忘れを防止したい

駅メモ!チームエンジニアの 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 configman 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 のバージョンに対応したドキュメントを読むことをおすすめします。