Merge remote-tracking branch 'origin/main'

This commit is contained in:
2026-04-29 13:20:36 +08:00
+152
View File
@@ -0,0 +1,152 @@
---
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]]