- レポジトリにsubmoduleの追加
- submoduleのあるレポジトリをcloneする
- submoduleの更新
- submoduleの削除
- ignore = dirty
- Submoduleのプロトコルの変更
レポジトリにsubmoduleの追加
git submodule add
で追加。
$ git submodule add [email protected]:rcmdnk/evernote_mail.git ./submodules/evernote_mail
add
すると.gitmodule
というファイルがまだ無い場合は作られ、その中に
[submodule "submodules/evernote_mail"]
path = submodules/evernote_mail
url = [email protected]:rcmdnk/evernote_mail.git
の様にsubmoduleのレポジトリのURLに加えて現レポジトリ内での位置情報が組み込まれます。
.git/config
の方にも
[submodule "submodules/evernote_mail"]
url = [email protected]:rcmdnk/evernote_mail.git
として情報が組み込まれます。
その後、通常の更新と同様
$ git add .gitmodules submodules/evernote_mail
$ # or git add -A
$ git commit -m "Added submodule:evernote_mail"
$ git push
とすれば、.gitmodule
ファイル、及び、上記で指定した
submoduleのディレクトリが親レポジトリへ登録されます。
GitHub上ではsubmoduleはそのレポジトリへのリンクになってみえます。
ref: https://github.com/rcmdnk/scripts/tree/master/submodules
submoduleのあるレポジトリをcloneする
この親レポジトリ(scripts)を他でcloneすると、
.gitmodule
だけにevernote_mailがあり、
evernote_mail
ディレクトリの中身は空のママ。
.git/config
にも記述がないまま。
これを取ってくるためには
$ git submodule init
$ git submodule update
または
$ git submodule update --init
をします。init
で.git/config
へsubmoduleの情報を登録し、
update
で登録されてた状態にするためclone
してきます(update --init
==init
+update
)。
コマンドの最後に
$ git submodule update --init submodules/evernote_mail
の様にディレクトリ名を付ければそのsubmoduleだけに対して行います。
追記: 2013/12/04
$ git clone --recursive [email protected]:rcmdnk/scripts
の様に、--recursive
をつけるとclone
後にgit submodule update --init
が自動的に行われるので、すべて取ってきて特に問題ない場合は
--recursive
付きで一気に持ってこれます。
追記ここまで
追記: 2015/06/16
さらにsubmodule自体にもsubmoduleがあるような場合も全部 cloneしたりupdateしたりするようにしたい場合には
$ git submodule update --init --recursive
と--recursive
のオプションを付けます。
追記ここまで
このupdate
がいまいち何に対するアップデートなのか分からなくて混乱してしまいますが、
- 親レポジトリに登録されているsubmoduleのレポジトリ情報に基づきsubmoduleをアップデートする。
と言う意味でいいかと思います。
下に書くように、update
は決して
- submodule自身のレポジトリと比較して最新にアップデートする。
ではありません。
なので、何らかの変更をsubmodule内で直接する時は注意が必要で、
submoduleで変更した後、いきなり親ディレクトリで
git submodule update
すると変更が全て上書きされて、
親ディレクトリに登録された状態、に戻されます。
submodule内で作業をした場合には、必ずadd
/commit
して、
さらに親ディレクトリからもadd
してsubmoduleの状態をきちんと更新しておく
必要があります。
submoduleの更新
submoduleのレポジトリが更新されてたりしても自動的には更新されません。
各submoduleの中で最新の状態にして、親レポジトリで再度git add
する必要があります。
$ cd submodules/evernote_mail
$ git pull
$ cd -
$ git add submodules/evernote_mail
$ git commit -m "updated evernote_mail"
これで登録されているレポジトリ情報も最新のものになります。
また、submoduleを直接pull
したりするコマンドはないみたいですが、
foreach
というコマンドがあって、これをすると全てのsubmoduleに対して同じコマンドを
打てるのでこれを使えば
$ git submodule foreach git pull
とすればOK。
また、親レポジトリをclone
した際に既にsubmoduleに更新がある場合、
submoduleがcloneされた後に、
以前のコミットの状態で無名ブランチ(detached HEAD)になってしまいます。
$ cd submodules/evernote_mail
$ git branch
* (detached from 7efd90a)
master
なので、その状態で素直にgit pull
すると
$ git submodule foreach git pull
Entering 'submodules/evernote_mail'
You are not currently on a branch. Please specify which
branch you want to merge with. See git-pull(1) for details.
git pull <remote> <branch>
Stopping at 'submodules/evernote_mail'; script returned non-zero status.
の様なエラーが出てしまいます。 ので、もし、全てのsubmoduleでmasterブランチを追ってる場合は
$ git submodule update --init
$ git submodule foreach git checkout master
な感じでupdateした後に全てを現在のmaster状態にしてあげれば良いかと思います。 (取ってきたところでも積極的に更新、開発したい場合。)
submoduleで特定のブランチを使いたい場合にも、submoduleの中で
$ cd submodules/evernote_mail
$ git checkout test_branch
等としてsubmoduleの状態を変更して親レポジトリでadd
&commit
すれば
このブランチがupdate
した際に取ってこられるようになります。
submoduleの削除
$ submodule_dir=submodules/evernote_mail
$ git submodule deinit ${submodule_dir}
$ # git config --file .gitmodules --remove-section submodule.${submodule_dir} # not needed for the latest git?
$ git rm ${submodule_dir}
最初のdeinit
でevernote_mailディレクトリ内を全て消して
.git/config
からも情報を消します。
追記: 2014/06/29
gitのバージョンをあげたせいか、上の方法で行うと
最後のrm
の所で
$ git rm $d
fatal: Please, stage your changes to .gitmodules or stash them to proceed
と言われてしまうようになりました。
$ git commit -m "removed evernote_mail"
$ git rm $d
rm 'submodules/evernote_mail'
warning: Could not find section in .gitmodules where path=submodules/evernote_mail
(-_-) $
と一度.gitmodule
への変更をcommit
してから消すとWarningは出ますが消せます。
一応.git/config
や.gitmodule
からはきちんと削除されてるし
これで問題無いとは思いますが。
ただhelpとかを読み返して試して見た感じ、上の様にgit config
で消さなくても良くて、
deinit
した地点で.git/config
から情報削除(現在のwork treeから削除、中身も消す)、
git rm
をすることでディレクトリを削除し.gitmodule
も掃除して情報を消す、
作業を行ってくれる様です。
なので、git config
の部分はスキップして(したほうが?)良いみたいです。
追記ここまで
次に、gitmodules
からも消してsubmoduleの登録を消します。
最後に空のディレクトリを消して終わりです。
もし、親レポジトリからは外したいがファイルは残しておきたい、と言う場合には
$ submodule_dir=submodules/evernote_mail
$ git config --remove-section submodule.$submodule_dir}
$ git config --file .gitmodules --remove-section submodule.${submodule_dir}
$ git rm --cached ${submodule_dir}
のように、最初にsubmodule deinit
を使う変わりgit config
で
.git/config
から削除だけして、
最後に--cached
オプションを付けることでファイルやディレクトリは残したまま
親レポジトリへの登録だけ外します。
追記: 2013/11/07
ごちゃごちゃと消す作業をしてるとゴミが残ってしまって、 改めてsubmoduleに加えようとする時
$ git submodule add [email protected]:rcmdnk/evernote_mail.git ./submodules/evernote_mail
A git directory for 'submodules/evernote_mail' is found locally with remote(s):
origin [email protected]:rcmdnk/evernote_mail.git
If you want to reuse this local git directory instead of cloning again from
[email protected]:rcmdnk/evernote_mail.git
use the '--force' option. If the local git directory is not the correct repo
or you are unsure what this means choose another name with the '--name' option.
等と出ることがあります。
これなんかを見るにやはりsubmoduleを消すのは一筋縄で行かない所もあるのかな、 と言った印象も受けます 1。
で、deinitをしたり.gitmodules
や.git/config
等から消したりしても
上手く行かなかったんですが、
結局、問題は.git/modules/submodules/evernote_mail
と言うディレクトリが残ってて、
これが新たにsubmoduleとし追加することを拒んでた模様。
このディレクトリを直接削除してsubmodule add
したら上手く行きました。
追記ここまで
ignore = dirty
追記: 2013/11/07
submodule内で何かファイルの変更があり、 それをsubmodule内ではcommitしたりする必要がない場合 (何らかの設定ファイルなどをレポジトリ自体に作ってしまったりするようなものの場合)、 親ディレクトリから見ると
$ git diff
diff --git a/submodules/evernote_mail b/submodules/evernote_mail
--- a/submodules/evernote_mail
+++ b/submodules/evernote_mail
@@ -1 +1 @@
-Subproject commit 601bbca665de752c956a23b61e3e54dd16d62764
+Subproject commit 601bbca665de752c956a23b61e3e54dd16d62764-dirty
こんなかんじのcommitにdirty
が付いたものなってしまいます。
この変更は親ディレクトリからgit add -A
などしても
駄目なので、この変更をsubmodule自体に反映させたい時は
submodule内でadd
、commit
、等してから
親ディレクトリでgit add -A
なりしてこの新しい状態を
登録し直してあげます。
ただ、変更が(何らかの設定ファイルのような)ローカルだけに必要な物の場合や 自分の管理でないものだったりする場合、
$ git config --file .gitmodules submodule.submodules/evernote_mail.ignore dirty
の様にして、.gitmodules
にignore = dirty
を加えます(直接書いても勿論OK)。
[submodule "submodules/evernote_mail"]
path = submodules/evernote_mail
url = [email protected]:rcmdnk/evernote_mail.git
ignore = dirty
これでこのsubmodule内で何らかのコミットされてない変更があっても 親ディレクトリからは無視するようになります。
追記ここまで
Submoduleのプロトコルの変更
-
そのうち
git submodule rm
みたいなコマンドが出来ないかな。。。 ただ、そういうwrapperコマンドみたいのが増えてきて実際やってること分からなく なると困る、っていう思想もありそうな。git pullのこの話の様な。