文章

Git中Unity 文件无法回滚或始终显示未暂存文件问题排查指南

Git中Unity 文件无法回滚或始终显示未暂存文件问题排查指南

Git 中 Unity 文件无法回滚或始终显示未暂存文件问题排查指南

1. 问题现象

Unity 工程中偶尔会遇到:

  • SourceTree 一直显示某个文件为 Modified.
  • 点击恢复后仍然出现.
  • 暂存、恢复反复无效.
  • Diff 看不到实际内容变化.
  • git restore 后 git status 仍然显示修改.

这种情况通常不是 SourceTree 本身的问题, 而是:

1
2
3
4
5
Git Index 中的文件状态
        +
Working Tree 中的实际文件
        +
.gitattributes 的规则

三者对文本/二进制或 CRLF/LF 的理解不一致.


2. 先按这个流程排查

假设异常文件为:

1
Path/File.meta

Step 1: 查看状态

1
git status --short

Step 2: 查看 EOL 和文件类型

1
git ls-files --eol -- "Path/File.meta"

常见结果:

1
2
3
i/lf    w/lf    attr/text eol=lf
i/crlf  w/crlf  attr/text eol=lf
i/-text w/-text attr/text=auto eol=lf

含义:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
i/...     Git Index 中保存的文件状态
          常见值:
          i/lf     = Index 中使用 LF
          i/crlf   = Index 中使用 CRLF
          i/-text  = Git 将 Index 中的文件视为二进制

w/...     Working Tree, 即本地磁盘上的实际文件状态
          常见值:
          w/lf     = 本地文件使用 LF
          w/crlf   = 本地文件使用 CRLF
          w/-text  = Git 判断本地文件为二进制

attr/...  当前 .gitattributes 最终匹配到的规则
          常见值:
          attr/text eol=lf       = 强制作为文本, 并要求使用 LF
          attr/text=auto eol=lf  = Git 自动判断文本/二进制, 文本使用 LF
          attr/-text             = 明确作为二进制处理

Step 3: 查看 .gitattributes 最终规则

1
git check-attr -a -- "Path/File.meta"

例如:

1
2
text: set
eol: lf

表示强制作为文本并使用 LF.

Step 4: 判断是不是只有换行符变化

未暂存:

1
git diff --ignore-space-at-eol -- "Path/File.meta"

已暂存:

1
git diff --cached --ignore-space-at-eol -- "Path/File.meta"

如果没有输出, 通常说明实际内容没有变化, 差异主要来自 EOL.


3. 根据结果处理

情况 A: Index 是 CRLF, 规则要求 LF

例如:

1
i/crlf w/crlf attr/text eol=lf

说明仓库历史中的文件还是 CRLF, 但当前规则已经要求 LF.

确认没有实际内容变化后:

1
2
git add --renormalize -- "Path/File.meta"
git status

再次检查:

1
git ls-files --eol -- "Path/File.meta"

理想状态应变成:

1
i/lf ...

确认后提交即可.


情况 B: Unity .asset 实际是 Binary, 却被强制当 Text

Unity .asset 不一定是 YAML 文本, Terrain 等资源可能包含二进制数据.

不建议:

*.asset text eol=lf

建议:

*.asset text=auto eol=lf

这样 Git 会自动判断:

1
2
YAML .asset   -> Text -> LF
Binary .asset -> Binary -> 不做换行转换

修改 .gitattributes 后:

1
2
3
git add .gitattributes
git add --renormalize -- "Path/File.asset"
git status

再验证:

1
2
git check-attr -a -- "Path/File.asset"
git ls-files --eol -- "Path/File.asset"

二进制 .asset 的正常结果可能是:

1
i/-text w/-text attr/text=auto eol=lf

情况 C: 只是普通文件修改

如果 Diff 确实存在代码或 Unity 数据变化, 那么它不是 EOL 问题.

需要恢复时直接:

1
git restore --source=HEAD --staged --worktree -- "Path/File"

4. 修改 .gitattributes 后如何让旧文件生效

.gitattributes 改变以后, 已经被 Git 跟踪的文件不会自动全部重新应用新规则.

针对单个文件:

1
git add --renormalize -- "Path/File"

确认没有问题后再提交.

如果确认整个仓库都需要统一规则:

1
2
3
git add --renormalize .
git status
git diff --cached --stat

不要执行完直接提交.

如果突然出现大量图片、FBX 或其他二进制资源发生修改, 应先停止并重新检查 .gitattributes.

如果要确认暂存内容是否只有 EOL 变化:

1
git diff --cached --ignore-space-at-eol

没有输出通常说明没有实际文本内容变化.


5. Unity 推荐的关键 .gitattributes 规则

完整配置可以按照工程需要扩展, 这里重点保留 Unity 最容易出现问题的部分:

* text=auto

*.cs              text eol=lf diff=csharp
*.shader          text eol=lf
*.compute         text eol=lf
*.cginc           text eol=lf
*.hlsl            text eol=lf
*.shadergraph     text eol=lf
*.shadersubgraph  text eol=lf

*.meta            text eol=lf
*.unity           text eol=lf
*.prefab          text eol=lf
*.mat             text eol=lf

# .asset 可能是 Text 或 Binary.
*.asset           text=auto eol=lf

*.png binary
*.jpg binary
*.jpeg binary
*.dll binary

核心原则:

1
2
3
明确是文本的 Unity 文件 -> text eol=lf
可能是文本或二进制      -> text=auto
明确是二进制            -> binary

6. core.autocrlf

检查当前设置:

1
git config --show-origin --get core.autocrlf

如果仓库已经通过 .gitattributes 明确规定 eol=lf, 通常应优先以 .gitattributes 作为仓库统一规则.

必要时可以为当前仓库设置:

1
git config core.autocrlf false

但它更适合作为辅助配置, 不应该替代 .gitattributes.


7. 嵌套 Git 仓库需要额外注意

例如:

1
2
3
Repository A
└── Packages/
    └── Repository B

如果 A 和 B 都在跟踪同一个物理文件, 那么这个文件实际上同时受到:

1
2
Repository A 的 Index / .gitattributes
Repository B 的 Index / .gitattributes

两套规则影响.

因此出现异常时应该分别进入两个仓库执行:

1
2
git ls-files --eol -- "文件"
git check-attr -a -- "文件"

有可能子仓库已经是 LF, 但父仓库历史 Index 仍然保存 CRLF.

此时应在真正出现问题的仓库中执行对应的 renormalize.


8. 不建议的处理方式

不要为了隐藏问题使用:

1
git update-index --assume-unchanged "文件"

也不要一开始就使用:

1
git reset --hard HEAD

更不要在大型旧工程中未经检查直接执行:

1
git add --renormalize .

正确顺序始终应该是:

1
2
3
4
5
6
7
8
9
10
11
12
13
定位异常文件
    ↓
检查 i / w / attr
    ↓
确认是否只有 EOL 差异
    ↓
修正 .gitattributes
    ↓
小范围 renormalize
    ↓
检查 Diff
    ↓
提交

9. 最常用命令

以后再次遇到”文件一直 Modified、恢复无效、Diff 又看不到变化”时, 优先执行:

1
2
3
4
git status --short
git ls-files --eol -- "文件"
git check-attr -a -- "文件"
git diff --ignore-space-at-eol -- "文件"

确认属于 EOL / Attribute 问题后:

1
git add --renormalize -- "文件"

最后再次验证:

1
2
git ls-files --eol -- "文件"
git status

重点记住:

1
2
3
i/...    = Git Index
w/...    = Working Tree
attr/... = .gitattributes

当这三者对文件类型或 EOL 的理解不一致时, 就容易出现 Unity 文件无法正常恢复、反复 Modified 等问题.

本文由作者按照 CC BY 4.0 进行授权