🐚

Git Submoduleが他のブランチをマージしたときに巻き戻るアレをCIで防いだ話

2021/12/14に公開約2,700字

エアークローゼット21新卒エンジニアの小林(@helicoir)です。
airClosetアドベントカレンダー15日目の記事となる本記事では、チーム開発でGit submoduleを使っているなら誰でも(n=1)遭遇する「他のブランチをマージしたときにGit Submoduleが前コミットに巻き戻る」あの現象をCIで事前に検知できるようにしたときの話をします!

巻き戻り現象

Git Submoduleについてはこちらをご覧ください↓

https://git-scm.com/book/ja/v2/Git-のさまざまなツール-サブモジュール

あんまりピンと来てない方のために、例を交えて説明してみます。

  • リポジトリAの中に、Git SubmoduleとしてリポジトリBが含まれている。また、リポジトリAのmasterにおいては、リポジトリBのソースの最新コミットSHA1が1e692c5になっている。
  • この状態で、masterから作業ブランチA、作業ブランチBがチェックアウトされる。
  • ブランチAではリポジトリBの最新コミットSHA1を1049b22に更新した。
  • ブランチBではリポジトリBの最新コミットSHA1を変えなかった。

上記を前提として、

①先にブランチAがmasterにマージされた後、ブランチBをmasterにマージしようとする。
②ブランチBに、masterとブランチBのsubmodule SHA1差分を適応するdiffファイルが作成される。

③何かしらのアクシデントでdiffファイルが適応されないまま、ファイルBがmasterにマージされる。

以上のようなことが起こると、古いSHA1がmasterに適用されてしまいます。

アクシデントについては、単純な作業ミス、チームメンバーのsubmodule運用に対する理解不足など、さまざまなものが挙げられます。また、レビュー段階でもPull Requestのファイル差分では下のような状態でしか表示されないため、古いSHA1に変わってしまう変更であることが見抜かれづらいことも原因のひとつです。

解決策

今回はこの問題に対して、CI上で以下のようなチェックを行うjobを噛ませることで対応しました。

  1. 現在のブランチをcloneし、git submodule updateを実行する
  2. submoduleリポジトリ内部に入り、git rev-parse HEADでSHA1を取得する
  3. 作業ブランチに入り、masterが持っているsubmodule SHA1を作業ブランチでも持っているかどうかを確認する。
    4. 持ってない場合はexit 1でfailさせる
  4. 以上のような処理を、そのリポジトリが含むsubmoduleの数だけ実行する

実際にはこんな感じ。

for SUBMODULE_REPOSITORY in "${SUBMODULE_REPOSITORIES[@]}"
do
  git checkout -q $DEFAULT_BRANCH && git submodule update -q
  cd $SUBMODULE_REPOSITORY
  DEFAULT_BRANCH_SHA1=`git rev-parse HEAD`
  cd ../

  git checkout -q $CURRENT_BRANCH && git submodule update -q
  cd $SUBMODULE_REPOSITORY
  CURRENT_BRANCH_SHA1=`git rev-parse HEAD`
  if [ "$(git branch --contains ${DEFAULT_BRANCH_SHA1} | grep HEAD)" = "" ]; then
    echo -e "repository: $SUBMODULE_REPOSITORY\ndefault-branch-SHA1: $DEFAULT_BRANCH_SHA1\ncurrent-branch-SHA1: $CURRENT_BRANCH_SHA1\n"
    IS_FAIL=1
  fi
  cd ../
done

masterのsubmodule上でgit rev-parseを実行し、取得したSHA1を変数DEFAULT_BRANCH_SHA1に格納しておき、作業ブランチ側ではgit branch --contains ${DEFAULT_BRANCH_SHA1}のような形で、該当SHA1を今いるブランチが含んでいるかどうかをチェックします。
含まれていた場合、結果に文字列HEADが含まれるため、これが存在するかをgrep HEADで確かめます。やや決め打ち気味でモヤモヤするのでもっといい方法があればコメントにて教えていただけると大変ありがたいです!!

おわりに

たくさん相談に乗っていただいた@takasp さん、その他アドバイスいただいたエアクロエンジニアメンバーにこの場を借りてお礼させてください。ありがとうございました!

株式会社エアークローゼットでは、ともに最高のプロダクトをつくる仲間を募集しています。このエンジニア採用サイト↓のように遊びゴコロ満載な仲間と環境で、あなたをお待ちしています!

https://corp.air-closet.com/recruiting/developers/

Discussion

ログインするとコメントできます