◆

Git autocrlf 拉取代码换行符 LF 与 CRLF 设置

2023-07-26 00:560

默认设置为 lf

git config --global core.autocrlf input

上述设置 windows 默认值是 true

  • true : git 在提交时自动替换行尾符 crlf 为 lf,windows 系统在签出代码时,再把 lf 转换为 crlf
  • input: git 在提交时自动替换行尾符 crlf 为 lf,签出代码时,不转换
  • false: git 在提交时不转换,提交 crlf 行尾符到仓库,签出时,不转换

三个值该怎么选

记住一个前提就好判断:仓库里应该统一存 LF。这不是洁癖,是因为 Git 的 diff 是按行比对文本的,仓库里 LF 和 CRLF 混着存,会出现「明明没改内容,diff 却显示整个文件全变了」的情况。

按这个前提:

  • Windows 开发者用 true:仓库存 LF,本地检出成 CRLF。适合还在用不认 LF 的老工具(早期的记事本、某些 Windows 专用编辑器)的情况。
  • Windows 开发者也可以用 input:仓库存 LF,本地也是 LF。现在 VS Code、WebStorm、Windows Terminal、PowerShell 全都能正常处理 LF,所以我更推荐这个——本地和仓库一致,少一层转换就少一类问题。
  • macOS / Linux 用 input:系统本来就是 LF,input 只是多一层保险(万一从别处粘进来 CRLF 内容,提交时会被纠正)。
  • false 基本别用:它等于完全不管,仓库里存什么全看每个人本地是什么,最终必然混乱。

更好的做法是 .gitattributes

core.autocrlf 是每个人本地的配置,团队里总有人没配或者配错。.gitattributes 提交进仓库,对所有人生效,优先级也高于 core.autocrlf,这才是可靠的方案:

# .gitattributes

# 让 git 自己判断哪些是文本文件,文本文件在仓库里一律存 LF
* text=auto eol=lf

# 显式指定某些类型(比 text=auto 的猜测更可靠)
*.js   text eol=lf
*.ts   text eol=lf
*.vue  text eol=lf
*.json text eol=lf
*.md   text eol=lf
*.css  text eol=lf

# Windows 批处理和 PowerShell 脚本必须是 CRLF,否则可能执行异常
*.bat  text eol=crlf
*.cmd  text eol=crlf
*.ps1  text eol=crlf

# 二进制文件明确标出来,禁止任何转换
*.png  binary
*.jpg  binary
*.woff2 binary
*.pdf  binary
*.zip  binary

几个属性的含义:

  • text:声明这是文本文件,参与换行符转换。
  • text=auto:让 git 检测文件内容(含 NUL 字节就当二进制),检测为文本的才转换。
  • eol=lf / eol=crlf:检出到工作区时用哪种换行符。仓库里始终是 LF。
  • binary:等于 -text -diff,完全不转换、也不做文本 diff。

* text=auto eol=lf 这一行是现在最常见的配置,效果是「不管谁在什么系统上,工作区和仓库都是 LF」。*.bat 那几行是必要的例外——CMD 解析 .bat 时对 LF 的兼容性不好,可能出现莫名的执行错误。

配置了 .gitattributes 之后要归一化一次

这是最容易漏的一步。新加的规则只对之后的提交生效,仓库里已经存成 CRLF 的文件不会自动变。要一次性纠正:

# 1. 先确保工作区干净(有未提交改动的话先 stash 或 commit)
git status

# 2. 清空 index,让 git 忘记所有文件的缓存状态
git rm --cached -r .

# 3. 按新规则重新添加,这一步会做换行符转换
git reset --hard
git add .

# 4. 提交这次归一化
git commit -m "chore: normalize line endings"

Git 2.16+ 有个更简洁的命令做同样的事:

git add --renormalize .
git commit -m "chore: normalize line endings"

这个提交的 diff 会非常大(所有原本是 CRLF 的文件全部显示改动),所以应该单独提交、不夹带任何功能改动,并且提前跟团队说好,避免别人 rebase 时撞上巨型冲突。

常见症状对照

warning: LF will be replaced by CRLF in xxx:这不是错误,只是告诉你 core.autocrlf=true 正在做转换。嫌烦可以关掉提示:

git config --global core.safecrlf false

但更好的处理是理解它在说什么,然后决定 autocrlf 该配什么值。

整个文件显示全部改动,但内容看着一样:典型的换行符问题。验证:

# 忽略换行符差异看 diff,如果输出为空就确认是换行符导致的
git diff --ignore-cr-at-eol

# 直接看文件的实际换行符($ 表示行尾)
cat -A src/a.ts | head -3
# ^M$ 结尾是 CRLF,只有 $ 是 LF

# 看 git 认为这个文件的属性是什么
git check-attr text eol -- src/a.ts

Shell 脚本报 bad interpreter: /bin/bash^M:.sh 文件被转成了 CRLF,#!/bin/bash 后面多了个 \r,系统把它当成解释器路径的一部分。在 .gitattributes 里加一行:

*.sh text eol=lf

ESLint / Prettier 报 Delete ␍:Prettier 的 endOfLine 默认是 lf,和本地的 CRLF 文件不符。两边对齐即可:

// .prettierrc
{ "endOfLine": "lf" }

配了 .gitattributes 的 eol=lf 之后这个报错自然就消失了,因为工作区里的文件本来就是 LF。反过来如果团队用 autocrlf=true(工作区是 CRLF),Prettier 就得配 "endOfLine": "crlf" 或 "auto"。

检查仓库里有没有混用

# 列出所有被 git 认为是文本、且仓库里存了 CRLF 的文件
git ls-files --eol | grep 'i/crlf'

git ls-files --eol 的输出里 i/ 是 index(仓库)中的换行符,w/ 是工作区的。理想状态是 i/lf,w/ 按你的 eol 设置。看到 i/crlf 或者 i/mixed 就说明该做一次归一化了。

💬留言板0

还没有留言,来发表第一条吧~

PAGE LOAD / LIVEGit autocrlf 拉取代码换行符 LF 与 CRLF 设置加载中