问题现象

在 Windows 上编辑仓库中的文件并执行 git status 时,文件显示为已修改(M),但 git diff 输出完全为空。执行 git add 后再执行 git commit,报错 nothing to commit, working tree clean

根因分析

Git 的三种行尾模式

模式

作用

LF(Line Feed, \n

Linux/macOS 行尾标准,仓库中推荐

CRLF(Carriage Return + Line Feed, \r\n

Windows 行尾标准

CR(Carriage Return, \r

旧版 macOS(已基本淘汰)

触发条件

  1. 仓库中的文件以 LF 存储(这是 Git 的默认行为)

  2. Windows 上的 core.autocrlf 默认为 true

  3. Git 检出文件时自动 LF → CRLF,提交时自动 CRLF → LF

  4. 当文件被编辑保存后,磁盘上的 CRLF 版本与索引中的 LF 版本被 Git 判定为"不同"

  5. git status 显示已修改,但 git diff 对比的是工作区 vs 索引的内容,而行尾差异在 diff 层被忽略,所以显示为空

  6. git add 时 Git 执行 CRLF → LF 转换,与索引版本一致 → nothing to commit

为什么会反复出现

每次通过 Trae 编辑规则文件后,编辑器都会以 CRLF 格式保存文件。Git 在工作区看到 CRLF,索引中是 LF,于是又标记为已修改——形成循环。

解决方案

创建 .gitattributes 文件

在仓库根目录创建 .gitattributes,显式声明行尾策略,覆盖 core.autocrlf 的默认行为:

* text=auto eol=lf

配置项含义

部分

含义

*

匹配所有文件

text=auto

Git 自动检测文件是否为文本文件,是则进行行尾转换

eol=lf

强制所有文本文件使用 LF 行尾(即检出时也不转为 CRLF)

相对于默认行为的优势

行为

默认(有 .gitattributes 前)

.gitattributes

仓库存储

LF

LF(不变)

检出到工作区

core.autocrlf=true → CRLF

eol=lfLF(不变)

git status 误报

频繁出现

不再出现

跨平台协作

依赖每个人本机配置

统一由仓库声明控制

关键步骤:关闭 core.autocrlf

.gitattributes + Windows 默认的 core.autocrlf=true 会产生配置冲突:

.gitattributes: * text=auto eol=lf     → 要求所有文本文件使用 LF
core.autocrlf: true                     → 等效于 * text=auto + core.eol=crlf(强制 CRLF)

根据官方 Git 文档:"If the eol attribute is specified, its line endings are determined by core.autocrlf or core.eol."——当 eol 显式指定时,core.autocrlf 不应决定行尾。但实际上 autocrlf=true 仍会产生 "LF will be replaced by CRLF" 警告噪音。关闭它是消除冲突的合理操作。

结果是:每次 git diff 都会报 CRLF 将替换为 LF 的警告。永久修复:

git config core.autocrlf false
git add --renormalize .

core.autocrlf false 关掉本地的 CRLF→LF 自动转换,交给 .gitattributes 统一管理。.gitattributes 会随仓库分发,自动覆盖所有克隆者的行为。

完整修复流程

# 1. 确保仓库根目录有 .gitattributes(未创建则先创建)
# 2. 关闭本地的 autocrlf
git config core.autocrlf false
# 3. 重写索引中的行尾记录
git add --renormalize .
# 4. 确认零警告
git diff --shortstat
# 输出为空即修复完成