--- 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]]