UTF-8なのにGitで全行が変更扱い?CRLFとLF・文字コードの違い

UTF-8なのにGitで全行が変更扱い?CRLFとLF・文字コードの違い

「CRLFとLFって何?UTF-8にそろえれば同じになるの?」

「1行しか直していないのに、Gitでは全部の行が変わっている…」

ファイルを保存して差分を開いたら、画面が変更表示で埋まっていた。
そんなときは、文章そのものではなく、見えない改行の目印が変わった可能性があります。

Gitはファイルの変更履歴を管理する道具です。
この記事では、CRLFとLFの意味から、Gitで全行差分が出る仕組み、変換前の確認方法まで説明します。
文字化けと改行の問題を分けて考えられると、必要な箇所だけを落ち着いて直せます。

目次

まずは結論から

UTF-8とCRLF・LFは、決めていることが違います。
UTF-8は文字をどんな数値の並びで保存するか、CRLFとLFは行の終わりをどう表すかに関わります。

  • CRLFとLFは改行の表し方
    同じUTF-8のファイルでも、どちらの改行も使えます。
  • 全行差分では行末を確認
    見える文字が同じでも、各行末のデータが変わると変更として扱われることがあります。
  • 変換前に保存ルールを確認
    まず差分と対象ファイルの設定を調べ、チームの方針に合わせます。

Gitは、設定に応じて改行を変換します。
保存される内容と手元のファイルで改行が違っていても、それだけで異常とは判断できません。

CRLFとLFの正体

テキストファイルには、画面に見える文字のほかに、改行を表す制御文字が入ります。
制御文字とは、「ここで行を区切る」といった働きを持つ、通常は文字として表示されないデータです。

LFはLine Feed、CRはCarriage Returnの略です。
もともとの意味は、LFが次の行へ進む動作、CRが行の先頭へ戻る動作に対応します。
CRLFは、その2つを続けた表し方です。

たとえば、タイプライターで「紙を次の行へ進める動き」と「印字位置を左端へ戻す動き」を考えると、2つの役割をイメージできます。
ただし、今のエディターは改行の並びを読み取って表示するため、実際の画面が機械のように動くわけではありません。

WindowsではCRLF、LinuxなどではLFがよく使われてきました。
現在はWindowsでもLFを扱う開発ツールが多く、OS名だけでファイルの改行が決まるわけではありません。
既存ファイルの内容、エディターの保存設定、プロジェクトの規則を見て判断します。

同じ2行でも異なるデータ

「Aの次に改行、Bの次にも改行」というファイルを考えます。
UTF-8で保存したとき、Aは16進数の41、Bは42です。
16進数は、ファイル内の数値を短く書くための表記と考えてください。

LF    41 0A 42 0A
CRLF  41 0D 0A 42 0D 0A

0AがLF、0DがCRに対応します。
画面では同じ2行に見えても、CRLFの側には改行ごとに0Dが加わっています。
これは「文字の見た目」と「保存されているデータ」が別だと分かる小さな例です。

CRLFとLFで同じ2行のUTF-8テキストを保存したバイト列の違い

UTF-8と改行コードの関係

UTF-8は、文字を数値の並びに変えて保存する文字コードの一つです。
「日本語をどんなデータで書くか」と「どこで行を区切るか」は別の選択なので、UTF-8とLF、UTF-8とCRLFの両方が成り立ちます。

ノートにたとえるなら、文字コードは文字を書くためのルール、改行は行を区切る目印です。
同じルールで書いた文章でも、行末の目印は変えられます。
そのため、UTF-8へ保存し直すだけで改行の問題も解決するとは限りません。

文字化けは、主に保存時と読み込み時で文字コードの解釈が食い違う問題です。
改行の変更は、日本語が正常に読めていても起こります。
文字そのものが崩れている場合は、こちらの説明も役立ちます。

あわせて読みたい
文字化けはなぜ起こる?原因と文字コードの仕組みを徹底解説 謎の文字列にうんざり…そんな経験ありませんか? メールやウェブサイトを見ていると、突然意味不明な記号の羅列が現れて「うわっ、文字化けだ!」と困惑した経験は誰に...

BOMは、ファイルの先頭に付くことがある、文字コードの識別などに使われる目印です。
UTF-8のBOMの有無と、行末がCRLFかLFかも別の話になります。
Excelで日本語が崩れる問題と全行差分を、同じ原因だと思わないようにしましょう。

あわせて読みたい
Excelの文字化けはもう怖くない!UTF-8のBOMあり・なしを徹底解説 「CSVファイルを開いたら、意味不明な文字の羅列が…」 そんな経験はありませんか。 実はそれ、文字コードの「BOM(ボム)」という仕組みが関係しているかもしれません。...
まとめると…

UTF-8は文字の保存方法、CRLFとLFは改行の表し方です。
文字が正しく読めることと、行末のデータが同じことは別々に確認します。

Gitで全行が変更扱いになる仕組み

Gitの差分は、画面に見える文章だけを見ているわけではありません。
行末のデータが変われば、見た目が同じ行も変更として現れることがあります。
ただし、改行変換の設定によって、比較する前に違いが吸収される場合もあります。

例えば、Gitのフォルダに3行のファイルを作り、変更を確認します。
改行の自動変換をしない設定でLFからCRLFへ変更すると、文字を変えていなくても差分は「3行追加・3行削除」になりました。
これは、すべての行の終わりが変わったためです。

手元のファイルと登録する内容の違い

ここで、Gitが扱う場所を3つに分けてみましょう。
リポジトリとは、ファイルや履歴を管理する単位です。

  • 作業ツリー
    普段エディターで開いて編集する、手元のファイルです。
  • インデックス
    次の履歴へ登録する内容を準備する場所です。ステージング領域とも呼ばれます。
  • コミット
    準備した内容を、変更履歴の一つとして記録することです。

git addで内容をインデックスへ登録するとき、設定に応じてCRLFをLFへそろえられます。
反対に、Gitから作業用ファイルを取り出すときは、LFをCRLFへ変換する設定もあります。
ファイルが置かれる場所によって改行を変える運用も、正常な使い方です。

CRLFとLFがGitの作業ファイルとインデックスで変換される流れ

検証でも、text eol=crlfという規則を指定すると、インデックスはLF、作業ファイルはCRLFになりました。
この状態では、Gitは未変更として扱いました。
「2つの場所で違う」ことより、設定した規則と一致しているかを見るのがポイントです。

変換する前の3つの確認

すぐ全ファイルを変換する必要はありません。
まず未保存の作業を保存し、心配なら作業ファイルを別の場所へコピーしてから確認します。
以下のコマンドは、Gitを使えるターミナルで、対象リポジトリのフォルダーを開いて実行してください。

1. 差分の中身を確認

demo.txtを実際の対象ファイル名に置き換えます。
ファイル名に空白がある場合も、そのまま引用符で囲んで指定できます。

git diff -- "demo.txt"
git diff --ignore-space-at-eol -- "demo.txt"

2行目は、行末の空白の違いを無視して比較します。
こちらだけ差分が消えるなら、改行や行末の空白が原因候補です。
行末のスペースも無視するため、「CRLFだけが原因」と確定するコマンドではありません。

すでにgit addした変更を見るときは、git diff --cachedを使います。
通常のgit diffは主に作業ファイルとインデックスの違いを見ているので、登録済みの変更だけなら何も出ないことがあります。

2. 各場所の改行を確認

git ls-files --eol -- "demo.txt"

Gitで管理中のファイルなら、次のような情報を確認できます。
これは検証で確認した出力を読みやすく整えた例です。

i/lf  w/crlf  attr/text eol=crlf  demo.txt

i/lfはインデックス側がLF、w/crlfは作業ファイル側がCRLFという意味です。
attr/の後ろには、そのファイルに適用される改行関連の属性が出ます。
mixedなら改行の混在を示しますが、何も出ない場合は対象のパスやGitの管理対象かを確かめてください。

Git公式のls-files解説(英語)でも、i/とw/がそれぞれ示す場所を確認できます。

※ リンク先は、Gitプロジェクトが公開しているコマンドの公式マニュアルです。

3. ファイルの規則と設定元を確認

git check-attr text eol -- "demo.txt"
git config --show-origin --get core.autocrlf

1行目でファイルのtextとeolの指定、2行目でGitの自動変換設定と設定元を確認します。
unspecifiedは、その属性に指定がない状態です。
core.autocrlfが未設定なら2行目は何も表示しないこともあるので、空欄をfalseと読み替えないでください。

CRLFとLFによる全行差分を変更内容・保存ルール・方針の順に確認する流れ

保存ルールに合わせた直し方

共同開発では、まずリポジトリ内の.gitattributesを確認します。
これは、ファイルごとのGitの扱いを指定し、チームで共有できる設定ファイルです。
エディター側の保存規則や、チームの作業手順も合わせて見ます。

たとえば、次の指定は、対象のテキストファイルをGitへ登録するときLFへそろえ、作業側もLFにする例です。
既存プロジェクトへそのまま追加する指示ではありません。

*.txt text eol=lf

作業側をCRLFにしたい規則なら、eol=crlfにします。
この場合も、Gitへ登録する対象テキストの改行はLFにそろえられます。
例外的なファイルや既存の記録内容もあるため、設定を足すだけで過去の全ファイルが変換済みになるとは考えないでください。

一方、core.autocrlfはGit側の自動変換設定です。
以下は、明示的なファイル属性などによる上書きがない場合の基本です。

値 Gitへ登録するとき 作業ファイルを取り出すとき
true テキストのCRLFをLFへ変換 LFをCRLFへ変換
input テキストのCRLFをLFへ変換 LFからCRLFへは変換しない
false この設定による自動変換をしない この設定による自動変換をしない

falseでも、.gitattributesで改行変換を指定していれば、その規則は働きます。
また、--globalでの変更はほかのリポジトリにも影響するため、一つの症状を直す目的で急に切り替えないようにしましょう。
規則の詳しい関係は、GitHubの日本語公式ガイドとGitの属性マニュアル(英語)で確認できます。

※ GitHubはGitのリポジトリを共有するサービスです。リンク先は提供元の公式ガイドと、Gitプロジェクトの公式マニュアルです。

意図せず変わった1ファイルなら、決められた改行へ戻し、必要な編集だけが差分に残るか確認します。
チーム全体で規則を変更するなら、通常の機能変更と改行の統一を分けてレビューする方法が役立ちます。
git add --renormalize .のような一括処理は多くのファイルを登録し直すため、合意と差分確認なしで実行しないでください。

まとめると…

選ぶ基準は「WindowsだからCRLF」ではなく、対象ファイルとチームの保存規則です。
差分、属性、設定元を確認してから、必要な範囲だけ直します。

よくある質問

  • 改行コードを変えると文字化けも直りますか?
    通常は別の確認が必要です。日本語が崩れている場合は文字コードの解釈を、全行差分なら改行や空白の変更を調べます。
  • LFとCRLFはどちらが正しいですか?
    両方とも使われます。実行するツールの要件と、プロジェクトの規則に合わせて選びます。
  • 末尾の改行と空行は同じですか?
    同じではありません。最後の文字列の後に改行が一つあることと、内容のない行をさらに置くことは区別します。
  • 警告が出たらファイルは壊れていますか?
    LFとCRLFの置換予定を知らせる警告だけなら、破損の証明ではありません。対象ファイルの規則と差分を確認し、想定した変換か判断します。

まとめ

CRLFとLFは、画面では見えにくい行末のデータです。
UTF-8へそろえていても、この部分まで一致しているとは限りません。

  • 文字コードと改行を分ける
    UTF-8、BOM、CRLF・LFは同じ設定ではありません。
  • Gitが扱う場所を分ける
    作業ファイルとインデックスの改行が違っていても、規則どおりなら正常です。
  • 一括変更より先に確認
    差分、各場所の改行、ファイル属性と設定元を順に見ます。

全行差分で迷っているなら、まず対象の1ファイルでgit ls-files --eolを確認してみましょう。

  • URLをコピーしました!
目次