始めに
既存のリポジトリ配下のサブディレクトリをGit submodule
で運用しようと思いましたら、思わぬところでつまずいてしまいました。
同じような人の助けになればと思い記事にしました。
Gitのバージョンは、
Git Shell git version 2.11.0.windows.3
(GitHub for Windwos 3.3.4.0)
になります。
要件・構成
既に存在しているリポジトリの配下のサブディレクトリを、サブモジュールで管理しなければいけなくなったとします。
変更前のリポジトリのディレクトリ構成は、以下のようなものです。
Parent
│ parent.txt
│
└─Child #これをサブモジュールにする
child.txt
またブランチは、以下の3つがあるものとします。
- master
- develop1
- develop2
サブモジュールの構成は、以下のように親モジュールの変更前と全く同じにします。
ここでは、単純化するためにChildには今までの変更履歴を残さずに新しいリポジトリを作ることにします。
Child
child.txt
下準備
まずは、既存のChildディレクトリをコピーして新しいリポジトリを作ります。
cd Parent
cp -r ./Child ../Child
cd ../Child
git init
git add -A
git commit -m "first"
git remote add origin <ChildRemoteURL>
git push -u origin master
既存のブランチを削除できる場合
なかなかそういう事はないのでしょうが、master以外のブランチを全て削除できるとします。
その場合は、以下のように単純な操作で行えます。
cd Parent
git checkout master
git branch -b develop1
git branch -b develop2
git rm -rf Child
git add -A
git commit -m "remove Child"
git submodule add <ChildRemoteURL>
git commit -m "add submodule"
git push --recurse-submodules=on-demand
ここでブランチを削除するコマンドの実行順序は重要ではありません。
プッシュの後に削除しても問題ありません。
開発作業を再開する前に、既存のブランチを削除し新しいブランチを作り直せば良いという事です。
これで今まで通りに運用することが出来ます。
既存のブランチを残す場合
まずは、ブランチを削除するのを除いて同じ操作をしてみます。
cd Parent
git checkout master
git rm -rf Child
git add -A
git commit -m "remove Child"
git submodule add <ChildRemoteURL>
git commit -m "add submodule"
git push --recurse-submodules=on-demand
ここまでは、問題なく操作できます。
git status
を見てみます
git status
On branch master
nothing to commit, working tree clean
リポジトリはクリーンとなっています。
ブランチの切り替え1
それでは、ブランチを切り替えてみます。
git checkout develop
error: The following untracked working tree files would be overwritten by checkout:
Child/child.txt
Please move or remove them before you switch branches.
Aborting
チェックアウトが中断されてしまいました。
そこで以下のコマンドを実行する必要があります。
mv Child Temp #git mv ではない
git checkout develop1
git merge master #git rebase master
git checkout develop2
git merge master #git rebase master
git checkout master
rm Child #git rm ではない
mv Temp Child #git mv ではない
詳しく解説します。
まずは、git mv
を使わずにかわりにOS標準のmvを使って、ChildディレクトリをTempなどに名前を変更して退避します。
OS標準のmvを使うのは、Childを削除したことをステージングさせないためです。
mvの後のgit status
は以下のようなメッセージを返します。
git status
On branch master
Changes not staged for commit:
(use "git add/rm <file>..." to update what will be committed)
(use "git checkout -- <file>..." to discard changes in working directory)
deleted: Child
Untracked files:
(use "git add <file>..." to include in what will be committed)
temp/
no changes added to commit (use "git add" and/or "git commit -a")
このまま、リポジトリがダーティになっている状態でも気にせずに更新する必要があるブランチにチェックアウトします。
そして、全てのブランチでマージ(リベース)してサブモジュールを導入した状態に更新します。
develop1、develop2にはtempが残りますがそのままにします。
全てのブランチを更新してmasterに戻るとgit status
は、以下のように変わっています。
git status
On branch master
Untracked files:
(use "git add <file>..." to include in what will be committed)
temp/
nothing added to commit but untracked files present (use "git add" to track)
そして空のChildディレクトリが作られているのでそれをOS標準のrmで削除します。
最後に、退避して置いたTempをChildにOS標準のmvで名前を変更します。
ここでgit status
は、
git status
On branch master
nothing to commit, working tree clean
となり、リポジトリがクリーンになっています。
この状態で再度develop1にチェックアウトすると、masterと同じのTempが消えたクリーンな状態になっています。
ブランチの切り替え2
git checkout -f
を使う場合は、以下のようなコマンドを実行します。
git checkout -f develop1
git merge master #git rebase master
git checkout -f develop2
git merge master #git rebase master
git checkout master
git submodule foreach git checkout .
まずは更新したいブランチにgit checkout -f
でチェックアウトしマージ(リベース)します。
全てのブランチの更新が終わったらmasterにチェックアウトします。
そうすると、git status
は以下のようになり、Childが空になっています。
git status
On branch master
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git checkout -- <file>..." to discard changes in working directory)
(commit or discard the untracked or modified content in submodules)
modified: Child (modified content)
no changes added to commit (use "git add" and/or "git commit -a")
Childの中身を元に戻すために、
git submodule foreach git checkout .
を実行します。
これでブランチの切り替えが正常に行えるようになります。
最後に
既存のリポジトリをサブモジュールで構成しなおすのは、珍しくなさそうな気がしますがすんなりいかないのに驚きました。
リポジトリがクリーンに見えてそうでなかったり、ダーティなはずがそうでなかったりとかなりモヤモヤします。
操作をかなり単純なブランチで行いました。
ブランチの状態に依っては上手く行かない事も出てくるのかもしれません。
実際の運用で問題が発生しなければ良いのですが・・・