Files
cs-note/hhs/CHORE/LF VS CRLF.md
T
2026-05-24 11:42:38 +08:00

153 lines
5.1 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
tags: [chore, git, text-formatting, cross-platform]
create time: 2026-04-29 20:30
---
# LF VS CRLF
## 概述
换行符是文本中最隐蔽的"地雷"。LF(`\n`)和 CRLF(`\r\n`)的选择直接影响 Git 版本控制、跨平台协作和文件处理工具的兼容性。本文从零讲透两种换行符的由来、差异以及如何在项目中统一管理。
## 正文
### 为什么会有两种换行符?
在早期计算机系统中,"换行"实际上是两个独立的动作:
| 动作 | ASCII 码 | LF | CR |
|------|----------|----|----|
| **Line Feed** — 移到下一行开头 | 10 (`\n`) | ✅ | ❌ |
| **Carriage Return** — 光标回到行首 | 13 (`\r`) | ❌ | ✅ |
这源于机械打字机的操作习惯:**先回车(CR),再换纸(LF)**。不同系统对这两个动作的组合方式不同:
```mermaid
graph LR
A["UNIX / Linux / macOS"] -->|"仅 LF (\n)"| B("现代标准")
C["Windows"] -->|"CR + LF (\r\n)"| D("向后兼容")
E["Classic Mac OS (≤9)"] -->|"仅 CR (\r)"| F["已废弃"]
style B fill:#c8f7c5
style D fill:#fff3cd
style F fill:#f8d7da
```
**思考一下**:既然 Unix 系的 LF 更简洁,为什么 Windows 没有跟随统一?
因为兼容性是历史包袱——大量已有的 .txt、.doc、二进制工具都依赖 `\r\n`,强行更改会导致灾难性的破坏。
### Git 中的自动转换(核心!)
Git 提供了一个关键配置来化解这个矛盾:`core.autocrlf`。这是团队协作中最需要注意的部分。
| 配置值 | 行为(检出时) | 行为(提交时) | 适用场景 |
|--------|--------------|--------------|---------|
| `true` | `.gitattributes` 匹配的文件从 CRLF→LF | 从 LF→CRLF | **Windows 用户** |
| `input` | 不做任何转换 | CRLF→LF | **Linux/macOS 用户** |
| `false` | 不做任何转换 | 不做任何转换 | 不推荐 |
**关键点**:无论你的本地系统用什么,**仓库中存储的永远是 LF**。这才是正确做法。
#### 实际效果演示
假设你在 Windows 上编辑一个 Go 源文件:
```bash
# Windows 用户,core.auticrlf=true
# 本地磁盘上看到的是:
echo "hello\r\nworld\r\n" # 编辑器保存的 CRLF
# 提交时,Git 自动转换:
git add main.go # 暂存区变为 LF
# 仓库里永远是这样:
echo "hello\nworld\n" # Git 对象的纯 LF
```
如果你在 Linux 上克隆这个仓库,你会得到 LF;如果你在 Windows 上 clone,你会得到 CRLF(由 Git 实时转换)。**所有人都不需要手动管理换行符。**
### 常见的踩坑场景
> [!WARNING] 常见陷阱
> 以下情况 `core.autocrlf` 无法介入,需要你手动处理。
**1. JSON / YAML 配置文件**
某些格式对换行极其敏感:
```json
{"message": "line1\nline2"} // \n 在 JSON string 中有特殊含义
```
如果你的 JSON 文件用 CRLF,解析器可能把 `\r` 当作内容的一部分,导致校验失败。
> [!TIP] 建议
> 配置文件应始终使用 LF。在 `.gitattributes` 中强制约束:
> ```
> *.json text eol=lf
> *.yml text eol=lf
> *.yaml text eol=lf
> ```
**2. Shell 脚本(`.sh`)**
Linux 上的 shebang 行必须干净:
```bash
#!/bin/bash # ⚠️ 如果这里是 \r\n,内核会找不到解释器
echo "hello" # 报错: /bin/bash^M: bad interpreter
```
> [!TIP] 建议
> - 所有 shell 脚本强制 LF,不要交给 Git 自动转换
> - 在 `.gitattributes` 中排除脚本文件的 CRLF 转换:
> ```
> *.sh text eol=lf ident
> *.bat text eol=crlf # BAT 例外,它本就运行在 Windows 上
> ```
**3. 二进制文件的误判**
如果一个图片/压缩包被错误标记为 `text` 类型,Git 会尝试转换其中的字节序列。如果恰好有连续的 `0D 0A`(即 `\r\n`),就会被篡改。
> [!CAUTION]
> 绝对不要让二进制文件经过 `autocrlf` 或 `eol` 转换。Git 默认不会转换二进制文件,所以只需确保别手动标记成 `text` 即可。
### 排查与修复工具
当团队中出现换行符混用时,这些命令能快速诊断:
```bash
# 查看当前全局设置
git config --global core.autocrlf
# 找出文件中隐藏的 CRLF
git diff --check # 会在末尾标注 [tabSpace] 警告
# 一次性修复整个项目的换行符
git ls-files -z | xargs -0 sed -i 's/\r$//'
# PowerShell 快速检测
Get-Content file.txt | Format-Hex | Select-Object -First 5
# 如果看到 0D 0A 组合 → 说明是 CRLF
```
### 编辑器层面的一致性
光靠 Git 不够,编辑器也必须配合。以下是主流编辑器的推荐配置:
| 编辑器 | 配置项 | 推荐值 |
|--------|--------|--------|
| VS Code | `files.eol` | `"auto"`(跟随 Git 设置) |
| VS Code | `files.autoTrimTrailingWhitespace` | `true` |
| Vim | `fileformat` | `unix`(配合 `.vimrc` 固化) |
| Neovim | `vim.opt.fileformat:set('unix')` | 同上 |
| Sublime Text | `default_line_ending` | `"unix"` |
> [!NOTE] 最佳实践
> **让 Git 做唯一的决策者**。编辑器设为 `auto` 模式,Git 说了算。这样不会因为个人偏好产生分歧。
## 关联笔记
- [[hhs/CHORE/Git 最佳实践.md]]
- [[hhs/CHORE/README.md]]