Git line endings — Mac/Linux와 Windows 사이 CRLF 지옥 끝내기
.gitattributes 한 파일로 모든 머신에서 같은 줄바꿈을 보장한다. core.autocrlf 함정과 정공법.
같은 저장소를 Mac/Linux와 Windows에서 같이 쓰면 거의 반드시 마주치는 문제 — CRLF vs LF. PR diff가 "한 글자도 안 바뀌었는데 1000줄 변경됨"으로 폭발하거나, 셸 스크립트가 \r: command not found로 깨지거나, 파이썬이 SyntaxError: invalid character 에러를 낸다.
이 가이드는 모든 OS에서 같은 줄바꿈을 보장하는 .gitattributes 정공법을 정리한다. 한 번 셋업하면 평생 안 본다.
TL;DR
- 저장소 root에
.gitattributes를 두고* text=auto eol=lf명시 core.autocrlf는 꺼두는 것이 정공법 — autocrlf보다 .gitattributes가 우선- Windows 셸 스크립트는 강제로 LF (
*.sh text eol=lf) - Windows 배치 파일은 강제로 CRLF (
*.bat text eol=crlf) - 한 번 통합 후
git add --renormalize .로 기존 파일 재정렬
사전 조건
- Git 2.10+ (
.gitattributeseol=지원) - 저장소가 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~ |
| 구식 Mac OS 9 | CR (\r) | 1984~2001 |
1.2 git의 자동 변환 옵션 — core.autocrlf
| 값 | 체크아웃 시 | 커밋 시 |
|---|---|---|
true (Windows 기본) | LF → CRLF | CRLF → LF |
input (Unix 권장) | 변환 없음 | CRLF → LF |
false | 변환 없음 | 변환 없음 |
문제는 — 사람마다 이 설정이 다르고, 새 머신에서 다른 값으로 깔리고, 사용자가 모르게 줄바꿈이 변환되어 "안 바꿨는데 바뀜" 사태가 발생한다.
1.3 정공법은 .gitattributes
저장소 안에 두고 모든 사람에게 같은 규칙 강제. core.autocrlf보다 우선.
2. .gitattributes 베이스라인 — 5분
저장소 root에 .gitattributes:
# Default — 모든 텍스트 파일은 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가 무의미한 파일
*.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를 추가하고 commit한 직후, 이미 잘못된 줄바꿈으로 들어가 있는 파일을 다시 정렬해야 한다.
3.1 renormalize
# 저장소 root에서
git add .gitattributes
git commit -m "feat: add .gitattributes baseline"
# 모든 파일을 .gitattributes 규칙에 맞춰 재정렬
git add --renormalize .
git status # 어떤 파일이 바뀌었는지 확인
git commit -m "chore: renormalize line endings"--renormalize는 working tree의 파일을 변경하지 않고, index만 새 규칙대로 저장. 다음 사람이 pull 받으면 자동 정렬.
3.2 큰 저장소 / 협업 중
--renormalize는 큰 PR을 만든다. 협업 중인 저장소면:
- 사람들과 시간 조율 ("X시 이후 push 자제")
- renormalize 머지 후 모두가 한 번
git pull - 또는 작업 브랜치들에서
git rebase origin/main로 자동 통합
3.3 작은 저장소 / 혼자
부담 없이 한 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로 만들었어도 LF로 정규화.
# 또는 .gitattributes만 신뢰
git config --global core.autocrlf falseinput을 Win에서도 권장하는 이유: 본인 작업 결과가 OS 기본값에 흔들리지 않고 항상 LF.
core.eol
git config --global core.eol lf체크아웃 시 default 줄바꿈. .gitattributes의 eol=이 우선.
5. 에디터 설정 — IDE도 같은 방향
VS Code (.vscode/settings.json 또는 user settings):
{
"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 CRLF 있음
# 또는
od -c scripts/build.sh | head -3
# 각 줄 끝에 \n 만 보여야 함. \r \n 같이 보이면 CRLF.6.2 저장소 전체
# CRLF가 섞인 파일 찾기
git grep --cached -lI $'\r' -- ':!*.bat' ':!*.cmd' ':!*.ps1'-I는 바이너리 제외. ps1/bat 같은 의도적 CRLF는 제외.
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로 커밋된 셸 스크립트가 WSL/Linux에서 실행. 해결:
# 빠른 수동 변환
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
.py 파일이 CRLF + UTF-8 BOM. .gitattributes로 LF 강제 + 에디터에서 BOM 제거.
sed -i '1s/^\xEF\xBB\xBF//' file.py7.3 .gitattributes가 실제로 안 통하는 경우
.gitattributes가 root에 없고 하위 디렉토리에 있는 경우 — 하위 디렉토리에만 적용- 파일이
--renormalize안 됐을 때 — 이미 들어간 줄바꿈은 그대로 (한 번 renormalize 필수)
7.4 IDE가 CRLF로 다시 저장
.editorconfig도 같이:
# .editorconfig (저장소 root)
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 체크리스트
기존 저장소를 정공법으로 가져오기:
-
.gitattributesbaseline 추가 (§2) -
.editorconfig추가 (§7.4) -
git add --renormalize .+ commit - CI에 CRLF 검사 추가 (§6.3)
- 팀에 공지 — "pull 받고
git config --global core.autocrlf input확인" - README에 한 줄 — "이 저장소는 LF 통일.
.gitattributes참고"
다음 단계
- Multi-OS 파일 동기화 — Git 외 파일도 같은 방향성
- Mac 초기 셋업 — git 글로벌 설정 베이스라인
- Windows 초기 셋업 — WSL2 git 설정
참고 링크
변경 이력
- 2026-05-12 — 초안 (devAlice M2 시드 확장)