vault backup: 2026-04-29 13:19:10
This commit is contained in:
@@ -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]]
|
||||||
Reference in New Issue
Block a user