2020年08月28日(金) [長年日記]
■ [dev][windows] Gitの改行コード自動変換の動作
Visual Studio 2017のGit機能を使ってリポジトリからファイルを取り出すと、ファイルの改行コードがCRLFに変換されてしまうことに気づいた。
Gitの core.autocrlf の設定はしていないはずなのにおかしいなと思ったが、Visual Studio 2017のGit機能は ~/.gitconfig は見ておらず Program Files (x86)\Microsoft Visual Studio\2017\Professional\Common7\IDE\CommonExtensions\Microsoft\TeamFoundation\Team Explorer\Git\etc\gitconfig の設定に従っているようだった。このファイルには「autocrlf = true」 が設定されている。Visual Studio上にはこの設定を変更する機能は見つけられなかった。
ではどうすればいいのか。
Gitの改行コード自動変換の動作をよく理解していなかったので、調べた結果をメモしておく。結構複雑だったので誤解している所があるかもしれない。もし間違いがあったら教えてください。
改行コード自動変換に影響する設定情報
改行コード自動変換に影響するのは次の設定情報だと考えればよさそう。
- ファイルのtext属性およびeol属性
- text属性の値ははtrue, false, auto, 未指定の4通り。
- eol属性の値はlf, crlf, 未指定の3通り。
- Gitクライアント用オプションのcore.autocrlf, core.eol
- core.autocrlfの値はtrue, false, inputの3通り。未指定の場合はfalseになる。
- core.eolの値はlf, crlf, nativeの3通り。未指定の場合はnativeになる。
ファイルの属性はリポジトリにコミットする .gitattributes ファイルにより設定できる。この情報は共有されるので全ユーザ共通の設定になる。 Gitクライアント用オプションはgit configコマンドにより設定する。この結果はリポジトリにはコミットされないのでユーザ毎に別々の設定になる。
改行コード自動変換の動作
改行コードの自動変換は、リポジトリへのチェックイン時とリポジトリからのチェックアウト時の2種類の動作がある。
リポジトリへのチェックイン時に行われるのは
- ファイルのCRLFをLFへ変換した結果をリポジトリへ格納する(これを「動作A」と呼ぶことにする)
だけであり、リポジトリからのチェックアウト時に行われるのは
- LFをCRLFへ変換したファイルを作業ディレクトリへ出力する(これを「動作B」と呼ぶことにする)
だけであると理解した。特に、チェックアウト時にCRLFをLFへ変換する機能は無いという理解。
設定情報と自動変換動作の関係
まず各ファイルが改行コード自動変換の対象となる(〇)かならない(×)かが次のようにして決まる。
- text属性がtrueの場合、〇。
- text属性がfalseの場合、×。
- text属性がautoの場合、どちらになるかGitが自動的に決める。
- text属性が未指定の場合、
- eol属性がlfまたはcrlfの場合、〇。
- eol属性が未指定の場合、
- core.autocrlfがtrueまたはinputの場合、どちらになるかGitが自動的に決める。
- core.autocrlfがfalseの場合、×。
ファイルが自動変換の対象となると決まった場合、次の変換処理が行われる。
- 動作Aは必ず行われる。
- 動作Bは次のケースでだけ行われる。
- eol属性がcrlf
- eol属性が未指定でcore.autocrlfがtrue
- eol属性が未指定でcore.autocrlfがfalseでcore.eolがcrlf
- eol属性が未指定でcore.autocrlfがfalseでcore.eolがnativeでプラットフォームの改行コードがcrlf(Windowsなど)
設定例
以上を踏まえると次のようになると考えられる。
改行コードの自動変換が行われないようにしたい場合
すべてのファイルのtext属性がfalseなら改行コードの変換は行われない。なので、次のような .gitattributes をコミットしておけばよい。
* -text
.editorconfig などにより改行コードを統一できている場合はこのような設定でいい気がする。
リポジトリ内の改行コードをLFに統一したい場合
必要なファイルに対し動作Aが起こるようにすればよく、そのためには必要なファイルが改行コード自動変換の対象となるようにすればよい。例えば次のようにする。
* text=auto
バイナリファイルが自動変換の対象にならないように注意する必要がある。
チェックアウト時にWindowsではCRLF、LinuxではLFになるようにしたい場合
Windowsでだけ動作Bが起こるようにすればよい。リポジトリ側でこの動作を強制することはできなさそうだが、上述のようにしてリポジトリ内の改行コードをLFに統一するようにしておけば通常のケースではうまくいきそう。
次のように設定しているユーザに対しては想定通りにならないが、このようなユーザはあまりいないと考えられる。
- Windows上でcore.autocrlfをinputに設定している
- Linux上でcore.autocrlfをtrueに設定している
- Linux上でcore.autocrlfをfalse, core.eolをcrlfに設定している
有益な情報ありがとうございます。
クライアントでcore.autocrlfがtrueとなっていても、「* -text」 を設定することで、core.autocrlfがfalse相当になるということでよいでしょうか。
そのような意図で書いていましたが、間違えているのかもしれません。core.autocrlf の説明
https://git-scm.com/docs/git-config#Documentation/git-config.txt-coreautocrlf
を見ると、「Setting this variable to "true" is the same as setting the text attribute to "auto" on all files」と書かれていますね。これはtext属性の指定を上書きするように読めます。もしそうなら、.gitattributes の指定が効かなそうに思います。
実際の所どうなのかは、試してみないとわからないです(記事を書いたときにこの動作を試したのかは覚えていません…)。
試してみました。本文に書いていたことは間違えてはいないのかなと思いました。次のようになりました。
1. リポジトリAに、改行コードがLFのテキストファイルBをコミットする。
2. 「git config --global core.autocrlf true」とした後、リポジトリAをcloneする。clone先のリポジトリでは、テキストファイルBの改行コードはCRLFになっていた。
3. リポジトリAに、「* -text」と記述した .gitattributes をコミットする。
4. core.autocrlf が true の状態のまま、リポジトリAを再度cloneする。clone先のリポジトリでは、テキストファイルBの改行コードはLFになっていた。
ただし、.gitattributesに「* -text」と記述するのがいいことかは疑問で、属性がtextでなくなることの悪影響が何かある可能性はあります。