Git提交文件换行符问题处理

前言

多人协作时经常会遇到一种很烦的问题。

明明没有改业务代码,git status 却提示很多文件已修改。

打开 diff 之后又看不出真实内容变化,提交记录里也全是无意义改动。

这类问题大多不是代码逻辑变了。

而是 LFCRLF 的换行符被编辑器或 Git 自动转换了。

本文主要解决这种“文件没改却显示可提交”的情况。

重点放在排查思路、仓库配置和一次性修正方法上。

原因

核心原因是不同系统默认换行符不一样。

系统差异

  1. Windows 常见的是 CRLF

  2. macOS 和 Linux 常见的是 LF

自动转换

Git 又提供了自动转换换行符的能力。

如果本地 Git 配置、编辑器配置、仓库规则三者不一致。

同一个文件在检出、编辑、提交时就可能被反复改写行尾格式。

常见触发点

常见触发点有这些。

  1. 某台机器开启了 core.autocrlf

  2. 项目里没有 .gitattributes 统一规则。

  3. 编辑器默认保存成了 CRLF

  4. 仓库里已经混入了不同换行符的历史文件。

排查

Git 配置

先看本机 Git 的换行符配置。

这一步能快速判断是不是全局配置在自动改文件。

1
2
git config --global core.autocrlf
git config --global core.safecrlf

仓库与编辑器

再看当前仓库是否已经有 .gitattributes

如果仓库已经定义了统一规则,优先以仓库规则为准。

1
dir .gitattributes

如果你使用的是 VS Code、Cursor、IDEA 等编辑器。

还要确认当前文件底部状态栏显示的是 LF 还是 CRLF

很多时候文件一保存就被编辑器改掉了。

解决方法

真正稳定的做法不是只改某一台电脑。

而是同时处理这三层。

  1. Git 不要随意自动转换。

  2. 仓库用 .gitattributes 明确规则。

  3. 编辑器用 .editorconfig 保持保存格式一致。

只做其中一层通常不够。

团队里只要有人环境不同,问题就会反复出现。

关闭自动转换

如果你希望本机不要偷偷改行尾。

可以先把全局 autocrlf 关闭。

下面这组命令适合想保留仓库统一规则的人。

safecrlf 使用 warn,可以在发现异常行尾时给出提醒。

1
2
git config --global core.autocrlf false
git config --global core.safecrlf warn

几个常见值的含义如下:

1
2
3
4
5
6
7
8
# 提交时转为 LF,检出时转为 CRLF
git config --global core.autocrlf true

# 提交时转为 LF,检出时不转换
git config --global core.autocrlf input

# 提交和检出都不自动转换
git config --global core.autocrlf false

如果你是跨平台团队开发。

通常更推荐 false + .gitattributes 的组合。

仓库规则

比全局 Git 配置更重要的是仓库内的 .gitattributes

因为它能把规则写进版本库里,让所有成员都遵守同一套标准。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
# 默认所有文件自动识别为文本,使用 LF 作为标准行尾
* text=auto eol=lf

# 前端开发
*.html text eol=lf
*.css text eol=lf
*.scss text eol=lf
*.sass text eol=lf
*.less text eol=lf
*.js text eol=lf
*.jsx text eol=lf
*.ts text eol=lf
*.tsx text eol=lf
*.vue text eol=lf

# 后端开发
*.java text eol=lf
*.kt text eol=lf
*.kts text eol=lf
*.py text eol=lf
*.go text eol=lf
*.rs text eol=lf
*.c text eol=lf
*.cpp text eol=lf
*.h text eol=lf
*.hpp text eol=lf

# 项目配置
*.xml text eol=lf
*.json text eol=lf
*.yml text eol=lf
*.yaml text eol=lf
*.toml text eol=lf
*.ini text eol=lf
*.conf text eol=lf
*.properties text eol=lf
*.gradle text eol=lf
*.env.example text eol=lf

# 脚本文件
*.sh text eol=lf

# Windows 脚本保留 CRLF
*.bat text eol=crlf
*.cmd text eol=crlf
*.ps1 text eol=crlf

# 二进制文件严禁转换
*.png binary
*.jpg binary
*.jpeg binary
*.gif binary
*.ico binary
*.svg binary
*.woff binary
*.woff2 binary
*.ttf binary
*.eot binary
*.pdf binary
*.zip binary
*.tar.gz binary

这种规则的特点很明确。

  1. 常见代码文件和配置文件会被强制统一成 LF

  2. mdtxtcsv、日志文件等其他文本内容不做强制限制。

  3. 对已有仓库更温和,改动范围通常比“全量文本统一”更小。

如果项目里还没有 .gitattributes

这一层仍然是解决问题的关键。

在更新 .gitattributes 后,必须由一个人执行全量规范化并提交,否则规则不会生效:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 1. 提交 .gitattributes 的修改
git add .gitattributes
git commit -m "chore: update .gitattributes for consistent line endings"

# 2. 强制刷新整个仓库的索引
git add --renormalize .

# 3. 如果有变更,提交规范化结果
# 检查是否有 staged changes
git status
git commit -m "chore: renormalize all files per new .gitattributes"

# 4. 推送到远程
git push

编辑器规则

为了避免开发者保存文件时又改回去了。

建议在项目根目录补一个 .editorconfig

这份配置会告诉常见编辑器统一使用 LF

同时顺手规范缩进和文件结尾空行。

1
2
3
4
5
6
7
8
9
10
root = true

[*]
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true

[*.{bat,cmd}]
end_of_line = crlf

如果团队成员使用的编辑器不同。

.editorconfig 会比口头约定可靠得多。

它虽然不能替代 .gitattributes

但能减少保存时再次污染文件的问题。

验证

处理完成后可以按下面顺序验证。

  1. 执行 git status,确认没有无意义变更。

  2. 新建或修改一个文本文件后保存,确认编辑器仍使用 LF

  3. 再次执行 git diff,确认只出现真实业务改动。

  4. 在另一台机器拉取仓库后重复检查一次。

如果你在 Windows 上开发。

尤其建议看一下编辑器右下角的换行符标识。

只要保存后又从 LF 变回 CRLF

说明编辑器层面还没有配好。

总结

文件明明没改却总是显示 Git 可提交。

大多数时候不是 Git 坏了。

而是换行符规则没有统一。

更稳妥的解决顺序是这样的。

  1. 本机关闭随意自动转换的 core.autocrlf

  2. 仓库增加 .gitattributes 统一代码文件和配置文件的行尾。

  3. 项目增加 .editorconfig 约束编辑器保存行为。

  4. git add --renormalize . 一次性修正历史行尾问题。

这样处理后。

后续提交记录会干净很多。

团队协作时也不容易再出现“整个文件都被改了”的假变更。