中身を1文字も変えていないのに、差分が全行変更として表示される。手元では動くシェルスクリプトが、別の環境では見つからないというエラーで止まる。こうした現象の正体は、たいてい行末に入っている改行コードの違いです。改行には歴史的な経緯から2つの表し方があり、OSや編集環境によって既定値が違うため、複数人やWindowsとその他が混ざる環境では必ずどこかで食い違います。やっかいなのは、画面上はまったく同じに見える点です。この記事では、症状から当たりをつけ、実際に入っている文字を確認し、再発を止めるところまでを順に紹介します。
1. 症状から改行コードを疑う
最初に、改行コードが原因のときに出やすい症状を知っておくと、調査が早くなります。代表的なものが3つあります。1つ目が、編集していないファイルの差分が全行変更として出ることです。行の中身ではなく行末が全行で変わっているため、差分としては全行が置き換わったように見えます。2つ目が、行末に見慣れない記号が表示される現象です。環境によっては制御文字が可視化され、行の終わりに余分な文字があるように見えます。3つ目が、シェルスクリプトを実行したときに、1行目に書いたインタプリタが見つからないという趣旨のエラーで止まることです。これは1行目の末尾に余分な制御文字が付き、実行するコマンドの名前の一部として解釈されてしまうために起きます。中身を変えていないのに挙動だけが変わったときは、まず行末を疑ってください。
2. ファイルを読んで、何が入っているかを確かめる
当たりがついたら、推測で変換をかける前に実物を確認します。ここで知っておくべき落とし穴が1つあります。ブラウザのテキスト入力欄は、仕様として貼り付けられた改行をすべてLFに置き換えます。つまりCRLFのファイルをコピーして入力欄に貼った時点で復帰文字は消えており、どんなに丁寧に調べても「復帰文字は入っていない」という誤った結論にしかたどり着けません。改行コードを調べるには、入力欄を経由せずファイルそのものをバイト列として読む必要があります。Line Ending Inspector はファイルを直接読み、CRLF、LF、単独のCRがそれぞれ何件あるかを表示します。ファイル全体が統一されているのか、一部の行だけ別形式が混ざっているのかが一度で分かり、混在している場合は少数派の行番号も出ます。あわせてBOMの有無と、最終行に改行があるかも表示されるので、見えない文字が原因の問題をまとめて確認できます。
この手順で使うツール
Line Ending Inspector
改行コードを検査 — CRLF / LF / CR の内訳と混在、BOMの有無
3. そろえたあとに、本当の差分だけを見る
改行コードが原因で差分が埋まっている状態では、レビューも原因調査も進みません。先に改行コードをそろえてから、あらためて中身を比較します。Diff Viewer に変更前と変更後を貼り付けると、追加行と削除行の件数がまとめて表示され、分割表示では行の中の文字単位の差分まで確認できます。ここで残った差分が、本当に意図して変更した部分です。なお、この比較に貼り付けを使えるのは、改行コードの違いが問題にならないからです。貼り付けた時点で両方ともLFにそろうため、見たい中身の差分だけが残ります。改行コードそのものを調べるには使えませんが、そろえたあとの確認には向いています。整形と機能の変更が同じコミットに混ざっていると後から履歴が読めなくなるので、改行コードをそろえる変更は単独のコミットに分けておくのが安全です。
この手順で使うツール
Diff Viewer
テキストの差分をリアルタイムで可視化
4. 行の中身についた余分な空白もあわせて片付ける
改行コードをそろえたあと、もう1つ残りやすいのが行末の余分な空白です。これも差分を汚す原因になり、改行コードと同時に混入していることがよくあります。Line Tools にテキストを貼り付けると、行の前後の空白除去、空行の削除、重複行の削除といった処理を行単位でまとめてかけられ、処理の前後で行数がどれだけ変わったかも表示されます。意図せず行が減っていないかを、その数字で確認できます。ここで注意したいのが、整形をかける範囲です。設定ファイルやデータファイルでは、行末の空白や空行に意味があることもあるため、全ファイルに一律でかけるのは避け、対象を絞って適用してください。また、この種の整形はまとめて行うと差分が大きくなるので、機能の変更とは別のコミットに分けておくと、後から履歴を読むときに困りません。
この手順で使うツール
Line Tools
行の並べ替え・重複削除・整形をまとめて処理
5. .gitattributes で再発を止める
手元で直しても、環境ごとの既定値が違うままなら同じことが繰り返されます。止める方法は2つあり、確実なのは後者です。1つ目が各自のGitの設定で変換の挙動を指定する方法ですが、これは個人の設定なので、新しく参加した人の環境には効きません。2つ目がリポジトリに .gitattributes を置く方法で、設定ファイル自体がリポジトリに入るため、関わる全員に同じ規則が適用されます。基本は、テキストファイルは自動判定に任せる指定を全体にかけたうえで、改行コードが固定されていないと困るファイルだけを個別に指定します。シェルスクリプトのように、復帰文字が入ると動かなくなるものは、改行だけの形に固定しておくのが安全です。逆に、Windowsの環境でしか使わないバッチファイルは、復帰付きの形に固定します。導入したあとは、既存ファイルを一度まとめて変換するコミットを入れ、そこから先は差分が出ない状態にそろえておきます。
まとめ
改行コードの問題は「症状から疑う → ファイルを読んで確かめる → そろえてから本当の差分を見る → 余分な空白も片付ける → .gitattributes で固定する」の順で進めると、場当たりの変換を繰り返さずに済みます。とくに押さえておきたいのは3つです。全行が差分になったらまず改行コードを疑うこと。調べるときは貼り付けではなくファイルを読むこと(入力欄に貼った時点で復帰文字は消えます)。そして、個人の設定ではなくリポジトリ側の設定で再発を止めること。この3つで、同じ事故が何度も戻ってくることはなくなります。