chmod +x しただけなのに Git で差分が出る理由と、core.filemode の正体
症状:chmod +x だけなのに差分扱いされる
例えば、次のような状況です。
chmod +x foo.sh # 中身は一切変更していない
git status
# 変更あり:
# modified: foo.sh
git diff を見ると、内容ではなく mode だけ変わっているのが分かります。
$ git diff foo.sh
diff --git a/foo.sh b/foo.sh
old mode 100644
new mode 100755
「中身は一文字も変えていないのに、なぜ差分になるのか?」というのが今回のテーマです。
Git が管理しているパーミッションは「x だけ」
Git はファイルの「パーミッション全部」を管理しているわけではありません。
read / write のビット(rw- の部分)は Git の履歴には保存されず、
「実行ビットが立っているかどうか」だけを記録します。
そのため
chmod +x foo.sh
としただけで、「中身は一文字も変えていないのに、差分があると認識されます」
.gitignore 対応できない
.gitignoreでは「x」の差分を無視するように書けません
core.filemode とは何か?
ここで関係してくるのが Git 設定項目 core.filemode です。
- core.filemode=true(デフォルト)
- ワークツリー上の実行ビットの変化を、変更として検出する。
- chmod +x foo.sh → git status で差分に出る。
- core.filemode=false
- 実行ビットの違いだけなら、差分として扱わない。
- 内容が変わらず、「644 ↔ 755 の違いだけ」の場合は無視される。
正しい対処法
core.filemode=true(デフォルト)
のまま、運用し。
「+x」が必要なファイルは、chmod +x をつけたうえで、コミット/プッシュし
この設定値をチーム内で共有する。
ただし、この方法は、最初にgit initしてファイルをプッシュして環境作るときに
いちいち、全部、必要なものを「+x」つけてプッシュすることになる。
その後、「+x」の付け忘れが発覚するたびに、コミット/プッシュすることになる。
非常に面倒である。
現実的な対処法
実行ビットの変更を Git で追跡したくない場合は、リポジトリ単位で
git config core.filemode false
を打ち込む
上記は、ls -aで直下に「.git」が見えるローカルのgitのルートフォルダで打ち込むこと。
その結果、
.git/config の中身の
filemode = true
が
filemode = false
に更新されます
内容が変わっていない
違いが「実行ビットだけ」
の変更は git status に出てこなくなります。
補足)
他の人と共有しているリポジトリでこの設定を入れると、
誰かが意図して付けた +x の差分も見落とすことになります。
チームで使う場合は方針を合わせてください。
おわり
諸事情があり、Linux環境のbashのシェルスクリプトをgitの資産として開発作業をする
機会がありました。
./foo.sh
などで打ち込んだ時に、パーミッションエラーがでて
chmod +x foo.sh
をしました。
別にfoo.shの内容を変更してない状況なのに、
git status で、foo.shが差分として表示され、不思議に思って調べたところ
当記事の内容がわかりました。
Linux環境のbashのシェルスクリプトをgitの資産として開発作業をして
「+x」を変更し、中身はまだ変更してない状況で、git statusの差分を見た
という事が、今までなかったため、知らなかった。
他のWebアプリなどでは、別にソースコードに「+x」などつける必要がない
ソースコードは基本的に読み書きするだけ。
また、
Window環境での*.batや、*.ps1の場合はLinuxでのパーミッションの概念ないです
そのため、それらをgitの資産として開発作業をしてる時は、気が付かなかった。
Discussion