LoginSignup
2
2

More than 5 years have passed since last update.

リポジトリ配下のサブディレクトリをサブモジュールに変更する

Last updated at Posted at 2017-08-15

始めに

既存のリポジトリ配下のサブディレクトリを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 .
を実行します。
これでブランチの切り替えが正常に行えるようになります。

最後に

既存のリポジトリをサブモジュールで構成しなおすのは、珍しくなさそうな気がしますがすんなりいかないのに驚きました。
リポジトリがクリーンに見えてそうでなかったり、ダーティなはずがそうでなかったりとかなりモヤモヤします。
操作をかなり単純なブランチで行いました。
ブランチの状態に依っては上手く行かない事も出てくるのかもしれません。
実際の運用で問題が発生しなければ良いのですが・・・

参考

Git-のさまざまなツール-サブモジュール#_サブモジュール使用時に気をつけるべきこと

2
2
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
2
2