.gitattributesで改行コードの扱いを制御する

Gitで管理するファイルの改行コードの扱いは、どうしたらよいのでしょうか。どうルール化するのがよいのでしょうか。

# なぜルールが必要か

そもそもなぜルールが必要なのでしょうか。以下の例で考えます。

AさんはLinuxマシンでコーディングしており、LFで改行されたソースコードのファイルXをGitにコミットしました。

一方、BさんはWindowsマシンでコーディングしています。今、Aさんが書いたコードを編集したいとします。GitからファイルXをチェックアウトして編集するわけですが、このとき、Bさんが使っているエディタはファイルXの改行コードをCRLFに変換してしまいました。Bさんはそれに気づかずコードを編集し、保存し、コミットしました。

BさんはファイルXの中の数行だけを編集したつもりなのに、改行コードが変わったために、全行に差分が生じてしまいました。これではコードレビューもしにくいし、後日、git blameコマンドを使っても望む結果は得られないでしょう。

このようなケースを考えると、「改行コードは各自適当でオッケー」とはいかないでしょう。

# 各自がcore.autocrlfを正しく設定すればいい？

Gitには[core.autocrlf](https://git-scm.com/book/en/v2/Customizing-Git-Git-Configuration)という設定があります。これの値をtrueにすると、Gitからワーキングディレクトリへファイルをチェックアウトするときに、改行コードはLFからCRLFに変換されます。逆にワーキングディレクトリからGitへコミットするときに、CRLFからLFに戻してくれます。

core.autocrlf=trueはWindowsユーザー向けの設定と考えてよいでしょう。「Windowsマシンでコーディングする人は、各自、core.autocrlfをtrueにしてね」と定めるのも一つの手でしょう。

しかし、誰かがその設定をうっかり忘れる可能性もありますし、何より面倒です。また、Windowsマシンであっても例外的にLFのままで扱いたいファイルも時にはあるでしょう。例えば、Cygwinを利用してWindowsマシン上でも実行したいシェルスクリプトがあったとして、core.autocrlfをtrueにしていたためにCRLFに変換されていたら困るかもしれません。

# .gitattributesを使ってGitリポジトリ毎にルールを定める

.gitattributesという名前のテキストファイルを作り、そこに[text属性](https://git-scm.com/docs/gitattributes#_checking_out_and_checking_in)に関する設定を書いてGitにコミットすれば、そのGitリポジトリを使う人全員で改行コードの扱いを統一できます。

とっかかりやすい実物として、上記のリンク先にあるサンプルを拝借し、これを題材に設定方法を理解していきます。これがそのサンプルです。

````
*           text=auto
*.txt		text
*.vcproj	text eol=crlf
*.sh		text eol=lf
*.jpg		-text
````

まず.gitattributesでは、前の行に書いた設定は、後ろの行に書いた設定によって上書きされるようになっています。というわけで、先頭の行には原則の設定、2行目以降には例外の設定が書かれています。順に見ていきます。まず1行目。

````
* text=auto
````

この行は、「全ファイルに、text属性にautoという特別な値を指定する」と定めています。「text属性がauto」とは何かというと、あるファイルを「これはテキストファイルだ」とGitが判定したら、改行コードの変換をするという指定になります（変換については後述します）。つまりautoにすると、変換するかしないかの判定をGitに委ねることになります。

続いて2行目を見てみます。

`*.txt text`

2行目は、「名前が*.txtにマッチするファイルなら、text属性にtrueを指定する」と定めています（単に属性名を書くことが、その属性にtrueを指定することになります）。text属性がtrueだと、必ず改行コードの変換をするという指定になります。

さて、改行コードの変換について具体的にいうと、

- チェックアウトするときは、もしユーザー毎の設定でcore.autocrlf=trueだったりしたら、LFからCRLFに変換することもしつつ、
- GitにコミットするときにはいずれにせよLFに統一します[^1]。

3、4行目も見てみます。

````
*.vcproj	text eol=crlf
*.sh		text eol=lf
````

text属性にtrueを指定している点はさっきと同じですが、さらにeolという属性も指定されています。eol属性にcrlfという値を指定すると、チェックアウトするときでもコミットするときでも、必ず改行コードをCRLFに統一するようになります。同様にlfという値を指定すると、必ずLFに統一します。例えば4行目の指定によって、シェルスクリプトのファイルは、たとえcore.autocrlf=trueにしているWindowsユーザーであっても、ワーキングディレクトリ内のファイルはLFのままになります。「*.shはとにかくLFで！」としているわけです。

最後の行を見てみます。

````
*.jpg -text
````

この行は2行目とは反対の意味になり、「名前が*.jpgにマッチするファイルなら、text属性にfalseを指定する」と定めています（属性名の手前に"-"を付けて書くことが、その属性にfalseを指定したことになります）。text属性がfalseだと、決して改行コードの変換をしないという指定になります。バイナリファイルの中にたまたまLFと一致する部分があって、それがCRLFに変換されたら困りますから、それを防いでいるわけです。

# .gitattributesを導入する際の指針

以上のように、ファイルの拡張子に応じて変換するかどうかを定め、一部の拡張子についてはCRLFかLFかも固定する、といった使い方が基本のようです。

今回の例のように、先頭に`* text=auto`を入れるかどうかは意見の分かれるところかもしれません。改行コードの変換をするかしないかの、Gitによる自動判定を信頼するかどうかによるからです。

信頼するならこの行を入れて、基本はGitに良きに計らってもらいつつ、念のため`*.jpg -text`のように、バイナリであることが既知である一部のファイルは明示的に除外しておくといった使い方が考えられます。`*.txt text`のような行は多分不要でしょう。Gitがうまく判断してくれそうです。

信頼しないならこの行は入れずに、`*.txt text`のような行をずらりと書き並べる使い方が考えられます。Gitによる判定に依存しないで済みますが、テキストファイルとして扱うための明示的な指定を書き漏らすと、誰かがうっかりCRLFでコミットしてしまうかもしれません。

[^1]: このことから、原則としてGitではLFで管理することを良しとしているのかなとも考えられます。ネット上でよく「基本、LFが良いらしい」といった記事を目にしますが、その根拠の一つがこれかもしれません。