改行コードCRLFとLFの違い|Git差分が全行変わる原因と対処法
「1文字も変更していないのに、Git で全行が差分として表示される」「メモ帳で開いたら改行されず1行になっている」——こうしたトラブルの原因は、たいてい改行コードの不一致です。目に見えない文字であるため、原因の特定に時間を取られがちな問題でもあります。
改行コードは3種類ある
改行は、テキスト中では制御文字として表現されます。歴史的な経緯から、3 つの方式が併存しています。
| 名称 | 文字 | 16進数 | 主な環境 |
|---|---|---|---|
| LF | \n | 0A | macOS、Linux、Unix |
| CRLF | \r\n | 0D 0A | Windows |
| CR | \r | 0D | 旧 Mac OS(9以前) |
CR は Carriage Return(復帰)、LF は Line Feed(改行)の略です。タイプライターで「印字位置を左端に戻す」動作が CR、「紙を1行送る」動作が LF に対応しています。Windows の CRLF は、この 2 つの動作を両方記録する方式です。
現在では、LF と CRLF の 2 つを区別できれば実務上は足ります。 CR 単独は旧 Mac OS のファイルでしか遭遇しません。
なぜ問題が起きるのか
ケース1:Gitで全行が差分になる
Windows と macOS が混在するチームで最も多いトラブルです。Windows のエディタが CRLF で保存し、macOS のエディタが LF で保存すると、内容が同じでも全行が「変更された」と判定されます。
改行コードは行末に含まれる文字であり、Git は行単位でバイト列を比較するためです。
対処は Git の設定で行います。
# Windows:コミット時にLFへ変換、チェックアウト時にCRLFへ戻す
git config --global core.autocrlf true
# macOS / Linux:コミット時にCRLFが混入していたらLFへ変換
git config --global core.autocrlf input
より確実なのは、リポジトリに .gitattributes を置いて、環境によらず統一する方法です。
* text=auto eol=lf
*.bat text eol=crlf
*.sh text eol=lf
*.png binary
text=auto eol=lf により、リポジトリ内では常に LF で保存されます。Windows のバッチファイルなど CRLF が必須のものだけ個別に指定します。
.gitattributes はリポジトリにコミットされるため、チーム全員に同じ設定が適用されます。 各自の core.autocrlf に依存するより安全です。
ケース2:テキストが1行に見える
古いメモ帳など、LF 単独の改行を解釈しないアプリケーションでファイルを開くと、改行されず 1 行に連結されて表示されます。
なお、Windows 10 以降のメモ帳は LF に対応しているため、この問題は減っています。ただし、業務システムの独自ビューアや古いツールでは依然として発生します。
ケース3:CSVを取り込むと行がずれる
CSV を他システムへ取り込む際、システム側が想定する改行コードと異なると、行の区切りが正しく認識されません。1 レコードが複数行に分割されたり、逆に連結されたりします。
取り込み仕様書に改行コードの指定がある場合は、必ずそれに合わせてください。指定がなければ CRLF が無難です(CSV の仕様を定めた RFC 4180 では CRLF が規定されています)。
ケース4:シェルスクリプトが実行できない
Linux 上で CRLF のシェルスクリプトを実行すると、次のようなエラーが出ることがあります。
/bin/bash^M: bad interpreter: No such file or directory
^M は CR を表します。1 行目の #!/bin/bash の末尾に CR が付いているため、/bin/bash\r という存在しないパスを探しに行って失敗しています。
シェルスクリプトは必ず LF で保存してください。
改行コードを確認する
改行コードは画面上では見えないため、確認には専用の手段が必要です。
エディタで確認する
VS Code はウィンドウ右下のステータスバーに LF / CRLF と表示されます。クリックすると変換もできます。
コマンドで確認する
# ファイル形式を判定
file script.sh
# → script.sh: Bourne-Again shell script, ASCII text executable, with CRLF line terminators
# CRの有無を目視で確認(^M として表示される)
cat -A script.sh | head
ブラウザで確認する
テキストを貼り付けるだけで判定したい場合は、改行コード変換ツールが使えます。含まれている改行コードの種類を判定し、LF / CRLF / CR への相互変換と、改行位置の可視化に対応しています。ブラウザ内で処理するため、業務データを貼り付けてもサーバーへ送信されません。
差分の原因が改行コードか切り分ける
「差分が出ているが、内容が違うのか改行コードだけの問題なのか分からない」という場合は、両方のテキストを同じ改行コードに揃えてから比較すると切り分けられます。
- 改行コード変換ツールで両方を LF に統一する
- テキスト差分比較ツールで比較する
- 差分が消えれば、原因は改行コードだけ
Git 上でも、git diff --ignore-cr-at-eol を使うと行末の CR を無視して比較できます。
文字数カウントへの影響
改行コードはファイルサイズにも影響します。CRLF は 1 行あたり 2 バイト、LF は 1 バイトを消費するため、1000 行のファイルでは 1000 バイトの差が生じます。
文字数制限のある入力欄や、バイト数で上限が決まっているシステムへ投入する場合は、この差が問題になることがあります。実際のバイト数は文字数カウントツールで確認できます。
実務での方針
迷ったときは、次を基準にしてください。
| 対象 | 推奨 |
|---|---|
| ソースコード全般 | LF |
| シェルスクリプト | LF(必須) |
| Windows バッチファイル(.bat / .cmd) | CRLF(必須) |
| CSV(連携仕様の指定なし) | CRLF |
| CSV(連携仕様の指定あり) | 指定に従う |
そして、プロジェクト開始時に .gitattributes を置くこと。後から統一しようとすると、一度は全ファイルが差分になる大きなコミットが必要になります。最初に決めておくのが最も安上がりです。
まとめ
- 改行コードは LF(Mac/Linux)と CRLF(Windows)の2つを押さえれば足りる
- Git の全行差分は改行コードの不一致がほぼ原因。
.gitattributesで統一する - シェルスクリプトは LF、Windows バッチは CRLF が必須
- 目に見えないため、変換ツールや
cat -Aで確認する - CRLF は LF より1行あたり1バイト多い
見えない文字が原因のトラブルは、疑うポイントを知っているかどうかで解決時間が大きく変わります。