131
116

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Git: Submodule地獄からの脱出

Last updated at Posted at 2014-07-18

git の submodule、便利ですが安定してない(変更の多い)リポジトリに使うと 地獄です。
Submodule Hellです。
やばいです。

開発で変更が入りまくるリポジトリをうっかりsubmoduleにしちゃった!
かなり開発して入り組んできたけどどうしよう…。

#なぜsubmoduleが地獄になるのか?

  • 更新が多いリポジトリはコミットが2箇所になって地獄
  • しかも変更がsha-1形式でしか分からなくて地獄
  • submoduleのクラス名・メソッド名等をリネームすると、親側もリネームが必要になって無駄に2回のコミットをしなくちゃいけなくて地獄。
    • さらに、親のコミットはコードの修正&submoduleの変更の取り込みも必要
  • submoduleの参照先同士が衝突した時、何が悪いのか探すのが大変すぎて地獄
  • いともたやすくsubmodule側が detached HEAD になって自分のコミットを見失うので地獄(reflogから復活とかある)
  • PullRequestの時に、何個も送らなきゃ行けなくて地獄
  • PullRequestをレビューする方も、見る箇所が別ページに行くの必須で地獄
  • ブランチの数も x submodule数で増加するので管理コスト増加で地獄
  • この悩みわかってない人に、「え、だって機能毎に分割してるんでしょ?いいじゃん」とか言われたりして地獄

####サブモジュールが素晴らしく見える理由

  • 分割統治
  • 複数の箇所で利用可能
  • 意味的に別リポジトリがスッキリする

まぁ、基本的に完全に安定しているライブラリをリンクして使う…みたいな使用法以外では使わないほうが良いと思います。

できれば月1回以下の更新が望ましい…。週1は辛い(フレームワークのBeta版とかだとよくある)

毎日submoduleを更新しなきゃいけない状況をsubmodule地獄と呼びます。(命名:俺。"Submodule Hell"で検索すると結構ありますが。)

基本的に submodule地獄になるような状況を最初から回避する ことが一番大切です。

いまsubmodule地獄にいるんですけど!?

地獄に入ってしまったらしょうがない。
脱出しましょう。

脱出法1:submoduleを親リポジトリに統合する方法

ようするに、親リポジトリとsubmoduleリポジトリに分かれているから良くない。
ひとつに統合しましょう。(参考

submoduleを親リポジトリに統合する

git rm --cached testSubModule
git rm .gitmodules 
rm -rf testSubModule/.git 
git add testSubModule

# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
# On branch master
# Your branch is up-to-date with 'origin/master'.
#
# Changes to be committed:
#       deleted:    .gitmodules
#       deleted:    testSubModule
#       new file:   testSubModule/README.md
#       new file:   testSubModule/...submoduleのファイルがたくさん...
#       new file:   testSubModule/...
#       new file:   testSubModule/...
#       new file:   testSubModule/...

##もう一度submoduleに分割する方法
安定版になった!submoduleに分割したい!という時には以下の方法で。

親リポジトリから削除します。

git rm -r --cached testSubModule

新しいリポジトリとして構築し直します。

cd testSubModule
git init
git add .
git commit -m "change to submodule"

これで新しいリポジトリを作成しました。
さらにremoteとして、元々使ってたsubmoduleのパスを追加します。

git remote add origin リモートリポジトリのパス(git@github.com:Xxx/testSubModule.git等)

pullし、おそらく衝突するので、強制的にローカルの内容で上書きし、コミットします。

git pull origin master
git checkout --ours .
git commit -a

最期にpushしてやれば終了です。

git push origin master

submoduleとして追加し直す場合は、 git rm testSubModule の後に親の階層に戻り、 git submodule add リモートリポジトリのパス とかすれば良いと思います。
 


さて、この方法ですが察しの良い方はお気づきと思いますが、この方法だと、当然ですが統合・再分割の過程において 履歴は消えます

「履歴もsubmoduleのほうに持って行きたい!><」とお考えの方には、残念ですが、現状だとそういう方法は 無いです… …あるけど、危ういです。

#脱出法2:履歴を持ったまま統合・そして分割する
git --filter-branchという最強のオプションを使います。
歴史を改ざんします。よく分かってないと怖いですね。

履歴を持ったまま、親リポジトリに統合する方法

こちら を参考にさせていただきました。

git filter-branch --index-filter \
  'git ls-files -s | sed "s@[[:cntrl:]]\"*@&testSubModule/@" |
      GIT_INDEX_FILE=$GIT_INDEX_FILE.new \
        git update-index --index-info &&
  mv $GIT_INDEX_FILE.new $GIT_INDEX_FILE || true' HEAD

mac以外の環境では[[:cntrl:]]部は、単に /t に置き換えてください。

作業ブランチを作成。

git checkout -b __work_for_import__

親ブランチに移動し、取り込む。
作業パス(cdの移動先)は環境に合わせて適切に。

cd ..
git remote add importSubmodule testSubModule/

リモート(いままでsubmoduleだったところ)から取得。
履歴を含んで testSubModule/ ディレクトリ以下に、submoduleのファイルが移ってきました。

git pull importSubmodule __work_for_import__

##履歴を持ったまま、submoduleに分割する方法

上記の逆です。

git filter-branch を使って、 testSubModule/ディレクトリ以外の変更を「なかったこと」にします。

git filter-branch --subdirectory-filter testSubModule/ HEAD

submoduleだったリポジトリにPUSHして終了。
(ただ、こういう時は新しくリポジトリを作ったほうが絶対安全です)

git push importSubmodule __work_for_import__

親リポジトリがすべてなかったことになってしまったので、修復をかけます。

git reset --hard HEAD^

あとは通常にsubmoduleを取り込んでください。
また、この処理はリポジトリを汚しまくる&処理時間がかなり掛かると思うので、注意して行ってください。

#まとめ

切出したり戻したりするの大変だから、安定版まで親リポジトリと一緒に育てて
submoduleが安定した時にディレクトリごと切り出せ

地獄から脱出する前に、地獄に来ないことをオススメします。

 
 

おわり

 

あ、あと git subtreeはこの問題についてある程度の解決策になります。
ただし、更新頻度が高くsubmodule間の依存性が高いような場合は、やはり親リポジトリに統合した方がよいでしょう。

131
116
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
131
116

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?