この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。
こんにちは。サービスグループの武田です。
バージョン管理システムであるGitはソースコードなどをリポジトリという単位で管理します。さらにGitにはサブモジュールという機能があり、これを利用することで別のリポジトリを自身のリポジトリ内で再利用できます。
とても便利なサブモジュール機能なのですが、サブモジュール内で何か変更などがあると親リポジトリ側でコミットができません。たとえばサブモジュール内でビルドなどをして成果物が生成され、それが.gitignore
に書かれていない場合などが該当します。とはいえ、サブモジュールでそれをコミットするわけにもいきません。
解決方法は主に2個あるようです。実際に再現させつつ、その解決方法を紹介します。
検証環境
次のような環境で検証しました。
$ sw_vers
ProductName: Mac OS X
ProductVersion: 10.14.3
BuildVersion: 18D109
$ git --version
git version 2.14.2
ローカルにリポジトリを準備する
まずは適当な作業ディレクトリに移動し、サブモジュールとなるsubmod-repo
リポジトリを作成します。適当にREADMEと成果物を生成するビルドスクリプトを追加してコミットしておきます。
$ cd /tmp
$ mkdir submod-repo && cd $_
$ git init
$ echo '# submod-repo' > README.md
$ git add README.md
$ git commit -m 'init commit'
$ cat > build.sh <<EOF
#!/bin/bash
mkdir build
echo 'build success' > build/artifact.txt
EOF
$ chmod 755 build.sh
$ git add build.sh
$ git commit -m 'add build.sh'
続いてsubmod-repo
リポジトリをサブモジュールとして利用するparent-repo
リポジトリを作成します。
$ cd /tmp
$ mkdir parent-repo && cd $_
$ git init
$ echo '# parent-repo' > README.md
$ git add README.md
$ git commit -m 'init commit'
これで準備ができました。それではparent-repo
リポジトリにsubmod-repo
リポジトリをサブモジュールとして追加します。追加にはgit submodule
コマンドを使用します。
parent-repo
$ git submodule add "$(cd .. && pwd)/submod-repo"
$ git status
On branch master
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
new file: .gitmodules
new file: submod-repo
$ git commit -m 'add submodule submod-repo'
$ git status
On branch master
nothing to commit, working tree clean
サブモジュールでファイルを追加してみる
サブモジュールで変更を発生させるため、サブモジュールとして追加したsubmod-repo
でbuild.sh
を実行してみます。
parent-repo
$ cd submod-repo
$ ./build.sh
$ cd ..
$ 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: submod-repo (untracked content)
no changes added to commit (use "git add" and/or "git commit -a")
$ git diff
diff --git a/submod-repo b/submod-repo
--- a/submod-repo
+++ b/submod-repo
@@ -1 +1 @@
-Subproject commit 18295850e3d1456cbda76626bf5b238b31a84394
+Subproject commit 18295850e3d1456cbda76626bf5b238b31a84394-dirty
もくろみどおりサブモジュールでuntrackedなファイルが追加されました。またgit diff
を確認してみると dirty が付いています。直接的な解決策としてはサブモジュール内でコミットしてしまうことですが、今回のようにビルドした成果物をコミットしたくはありません。
--ignore-submodulesオプションを使用する
というわけで、ファイルが追加されてしまうことはしかたがないとして、親リポジトリで操作する際にサブモジュールの変更を無視できればよさそうです。これには--ignore-submodules[=<when>]
オプションが利用できます。<when>
にはnone
、untracked
、dirty
、all
が指定できます。詳しくはドキュメントを参照してください。
Git - git-status Documentation
さっそくオプションを指定してコマンドを実行してみます。
parent-repo
$ git status --ignore-submodules=dirty
On branch master
nothing to commit, working tree clean
$ git diff --ignore-submodules=dirty
たしかに差分が出なくなりました!
.gitmodulesにignoreを追加する
オプションを指定すれば変更を無視できることがわかりました。しかし毎回オプションをつけるのは正直面倒です。また共同開発をしている場合、作業者全員に毎回オプションを付けろというのもなかなかのカオスです。そこで便利なのが.gitmodule
にignore = dirty
の設定を追加する方法です。次のようにgit config
コマンドを使用するか、直接ファイルを修正してもOKです。
parent-repo
$ git config --file=.gitmodules submodule.submod-repo.ignore dirty
$ git diff
diff --git a/.gitmodules b/.gitmodules
index 675a37d..c89ccaa 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -1,3 +1,4 @@
[submodule "submod-repo"]
path = submod-repo
url = /tmp/submod-repo
+ ignore = dirty
修正したファイルをコミットして、あらためて差分を確認してみます。
parent-repo
$ git add .gitmodules
$ git commit -m 'add submodule ignore dirty'
[master a433fc6] add submodule ignore dirty
1 file changed, 1 insertion(+)
$ git status
On branch master
nothing to commit, working tree clean
オプションを付けなくてもサブモジュールの差分が出なくなりました!素敵!
まとめ
サブモジュールで変更が出てしまうケースの解決方法をお届けしました。本音を言えば、こういった設定をしなくても済むのが理想ですね。自分が管理するリポジトリでは、コミットに含めないファイルなどは.gitignore
で指定しておきたいですね。