From bcba23838f7a5bb5a35a54ca4f280fea38271afc Mon Sep 17 00:00:00 2001 From: huanghaosheng <386998068@qq.com> Date: Wed, 29 Apr 2026 13:19:10 +0800 Subject: [PATCH] vault backup: 2026-04-29 13:19:10 --- hhs/CHORE/LF VS CRLF.md | 152 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 152 insertions(+) create mode 100644 hhs/CHORE/LF VS CRLF.md diff --git a/hhs/CHORE/LF VS CRLF.md b/hhs/CHORE/LF VS CRLF.md new file mode 100644 index 0000000..bae7721 --- /dev/null +++ b/hhs/CHORE/LF VS CRLF.md @@ -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]]