Git 改行コード — Mac/Linux と Windows の CRLF 地獄を終わらせる
`.gitattributes` 1枚でどこでも改行コードを統一する。core.autocrlf の罠と正しいやり方。
同じリポジトリを Mac/Linux と Windows で共有していれば、ほぼ確実にこれに遭遇する — CRLF vs LF。何も変えていないのに PR の diff が「1000行変更」に膨れ上がり、シェルスクリプトが \r: command not found で壊れ、Python が SyntaxError: invalid character を吐く。
このガイドは .gitattributes を使ってすべての OS で同じ改行コードを保証する正しい方法だ。一度設定してしまえば、二度と気にしなくて済む。
TL;DR
- リポルートに
.gitattributesを置き* text=auto eol=lfと書く core.autocrlfをオフにする —.gitattributesがどうせ上書きする- Windows シェルスクリプトには LF を強制(
*.sh text eol=lf) - Windows バッチファイルには CRLF を強制(
*.bat text eol=crlf) - 統一後は
git add --renormalize .を実行して既存ファイルを揃える
前提条件
- Git 2.10+(
.gitattributesのeol=をサポート) - Mac/Win ユーザーが同時に push するリポジトリ
1. 問題 — CRLF がなくならない理由
1.1 OS デフォルトの改行コード
| OS | 改行コード | 歴史 |
|---|---|---|
| Unix · macOS · Linux | LF (\n、0x0A) | Unix 1970年〜 |
| Windows | CRLF (\r\n、0x0D 0x0A) | DOS 1980年〜 |
| Classic Mac OS 9 | CR (\r) | 1984〜2001年 |
1.2 git の自動変換 — core.autocrlf
| 値 | チェックアウト時 | コミット時 |
|---|---|---|
true(Windows デフォルト) | LF → CRLF | CRLF → LF |
input(Unix 推奨) | なし | CRLF → LF |
false | なし | なし |
問題は、ユーザーごとに設定がバラバラで、新しいマシンではデフォルトのまま使われ、サイレントな変換が「何も変えていないのに diff が出た」という事態を引き起こすことだ。
1.3 正しいやり方は .gitattributes
リポジトリ内に置くことでチーム全員に同じルールを強制できる。core.autocrlf の個人設定を上書きする。
2. .gitattributes ベースライン — 5分
リポルートの .gitattributes:
# デフォルト — すべてのテキストファイルを LF で保存し、テキスト/バイナリを自動判定
* text=auto eol=lf
# 明示的なテキスト — LF 強制
*.c text eol=lf
*.cc text eol=lf
*.cpp text eol=lf
*.h text eol=lf
*.hpp text eol=lf
*.cs text eol=lf
*.go text eol=lf
*.java text eol=lf
*.kt text eol=lf
*.py text eol=lf
*.rb text eol=lf
*.rs text eol=lf
*.swift text eol=lf
*.ts text eol=lf
*.tsx text eol=lf
*.js text eol=lf
*.jsx text eol=lf
*.mjs text eol=lf
*.cjs text eol=lf
*.json text eol=lf
*.jsonc text eol=lf
*.yml text eol=lf
*.yaml text eol=lf
*.toml text eol=lf
*.md text eol=lf
*.mdx text eol=lf
*.txt text eol=lf
*.html text eol=lf
*.css text eol=lf
*.scss text eol=lf
*.svg text eol=lf
*.xml text eol=lf
*.sql text eol=lf
# シェルスクリプト — LF(CRLF で壊れる)
*.sh text eol=lf
*.bash text eol=lf
*.zsh text eol=lf
*.fish text eol=lf
Dockerfile* text eol=lf
Makefile* text eol=lf
# Windows 専用 — CRLF
*.bat text eol=crlf
*.cmd text eol=crlf
*.ps1 text eol=crlf
*.psm1 text eol=crlf
# バイナリ — 変換なし
*.png binary
*.jpg binary
*.jpeg binary
*.gif binary
*.ico binary
*.webp binary
*.avif binary
*.pdf binary
*.zip binary
*.gz binary
*.tar binary
*.7z binary
*.exe binary
*.dll binary
*.so binary
*.dylib binary
*.woff binary
*.woff2 binary
*.ttf binary
*.otf binary
*.mp3 binary
*.mp4 binary
*.mov binary
# diff を静かにする — テキストだが diff は無意味
*.lock text eol=lf -diff
pnpm-lock.yaml text eol=lf -diff
package-lock.json text eol=lf -diff
yarn.lock text eol=lf -diff
Cargo.lock text eol=lf -diff⚠️ PowerShell ファイル(
*.ps1)は CRLF が必要。Windows PowerShell は LF のみの ps1 を拒否することがある。
3. 既存リポジトリへの適用 — 5分
.gitattributes を追加してコミットし、すでにずれている改行コードを揃え直す。
3.1 renormalize
# リポルートで
git add .gitattributes
git commit -m "feat: add .gitattributes baseline"
# .gitattributes に従って全ファイルを揃え直す
git add --renormalize .
git status # 何が変わったか確認
git commit -m "chore: renormalize line endings"--renormalize は作業ツリーには触れず、インデックスだけを書き換える。他のメンバーは pull 時に自動的に揃う。
3.2 大規模リポジトリ / アクティブなコラボレーション
--renormalize は巨大な PR を生む。事前に調整しよう:
- 時間枠を設ける(「X 以降は push を止める」)
- renormalize をマージして全員が一度
git pullする - またはフィーチャーブランチで
git rebase origin/mainして吸収する
3.3 小規模リポジトリ / 個人
1つの PR としてそのままマージする。
4. ユーザーのグローバル設定 — core.autocrlf を鎮める — 1分
.gitattributes があるリポジトリでは core.autocrlf は実質的に意味をなさない。ただし .gitattributes がないレガシー・外部リポジトリでは依然として重要になる。推奨設定:
# macOS / Linux
git config --global core.autocrlf input
# チェックアウト時に変換なし、コミット時に CRLF → LF
# Windows
git config --global core.autocrlf input
# Windows でも input を推奨 — ファイルは常に LF でコミットされる。他者の CRLF も正規化される。
# または .gitattributes だけを信頼する
git config --global core.autocrlf falseWindows でも input を推奨する理由:OS デフォルトに左右されず、常に LF でコミットされるため一貫性が保てる。
core.eol
git config --global core.eol lfチェックアウト時のデフォルト改行コードを指定する。.gitattributes の eol= 設定が優先される。
5. エディタ設定 — IDE を揃える
VS Code(.vscode/settings.json またはユーザー設定):
{
"files.eol": "\n",
"files.insertFinalNewline": true,
"files.trimTrailingWhitespace": true
}JetBrains:Settings → Editor → Code Style → Line separator → Unix and macOS (\n)。
Vim:
" ~/.vimrc
set fileformats=unix,dos
set fileformat=unix6. 検証 — 本当に LF になっているか?
6.1 単一ファイル
# macOS/Linux
file scripts/build.sh
# ASCII text ← OK
file scripts/build.sh | grep -q CRLF && echo has CRLF
# または
od -c scripts/build.sh | head -3
# 行末が \n のみ。\r \n が見えたら CRLF が残っている。6.2 リポ全体
# CRLF を含むファイルを検索
git grep --cached -lI $'\r' -- ':!*.bat' ':!*.cmd' ':!*.ps1'-I はバイナリを除外。意図的な CRLF ファイル(ps1、bat)を除外する。
6.3 CI ゲートを追加する
GitHub Actions:
- name: Check no CRLF in LF files
run: |
crlf=$(git grep --cached -lI $'\r' -- ':!*.bat' ':!*.cmd' ':!*.ps1' || true)
if [ -n "$crlf" ]; then
echo "CRLF found in LF-expected files:"
echo "$crlf"
exit 1
fi7. よくある落とし穴と修正
7.1 .sh が Windows で ^M: command not found になる
CRLF のシェルスクリプトがコミットされてしまっている。応急処置:
dos2unix scripts/build.sh
# または
sed -i 's/\r$//' scripts/build.sh根本的な修正:.gitattributes に *.sh text eol=lf を追加して git add --renormalize を実行する。
7.2 Python の SyntaxError: invalid character
CRLF + UTF-8 BOM の .py ファイル。.gitattributes で LF を強制 + エディタで BOM を削除する。
sed -i '1s/^\xEF\xBB\xBF//' file.py7.3 .gitattributes が実際に適用されていない
.gitattributesがルートではなくサブディレクトリにある — そのサブディレクトリのみに適用される点に注意- ファイルが renormalize されていない — 既存の改行コードが残り続ける(
git add --renormalize .を実行する)
7.4 IDE が CRLF で再保存する
.editorconfig を追加する:
# .editorconfig(リポルート)
root = true
[*]
end_of_line = lf
charset = utf-8
indent_style = space
indent_size = 2
insert_final_newline = true
trim_trailing_whitespace = true
[*.{bat,cmd,ps1}]
end_of_line = crlfVS Code、JetBrains、Vim、Sublime など多くのエディタが .editorconfig を自動で読む。
7.5 Windows エディタが自動的に BOM を追加する
PowerShell の Out-File はデフォルトで UTF-16 BOM を付加する。UTF-8 を強制するには:
$content | Out-File "file.txt" -Encoding utf8 -NoNewline
# または PowerShell 7+
$content | Out-File "file.txt" -Encoding utf8NoBOM8. 既存リポジトリを修正する PR チェックリスト
-
.gitattributesベースラインを追加(§2) -
.editorconfigを追加(§7.4) -
git add --renormalize .+ コミット - CI CRLF チェックを追加(§6.3)
- チームに通知 — 「pull 後に
git config --global core.autocrlf inputを確認してください」 - README に1行 — 「このリポジトリは LF に統一している。
.gitattributesを参照してください。」
次のステップ
- マルチOS ファイル同期 — Git 以外のファイルも
- Mac 初期設定 — git グローバルベースライン
- Windows 初期設定 — WSL2 git セットアップ
参考資料
更新履歴
- 2026-05-12 — 初稿(devAlice M2 シード展開)