前言

在日常开发的时候,常常会遇到这样两种情况:

1. 模块/路径找不到(线上崩溃)

本地开发、测试一切正常,代码合并提交一气呵成。然而,一到线上 CI/CD 部署构建,或者同事拉取代码,就直接报“找不到模块/路径”的错误崩溃了!

排查了半天,你一拍大腿:原来是自己把文件夹 app/components/layout 改成了 app/components/Layout(首字母 l 改为了大写 L),或者把 utils.js 改成了 Utils.js,而 Git 居然完全没有检测到这个修改!

2. 目录名或文件名修改没反应

有时候我们可能会将目录名或文件名由不规范的命名方式改成规范的命名方式,比如 components/layout 改成 components/Layout

但是你会发现修改后,Git 却没有检测到任何修改git status 提示没有任何变更,你无法正确提交代码,这很蛋疼。

一直以来我一直被这些问题所困扰,直到最近,我终于是搞清楚了怎么回事,并找到了最完美的解决方案。


为什么 Git 默认对大小写不敏感?

其实,Git 本身(诞生于 Linux)是高度敏感大小写的。之所以在你的电脑上“不敏感”,是因为操作系统在和 Git 玩“双簧”

  • Linux:默认大小写敏感。在 Linux 眼里,layoutLayout 是两个完全不同的文件夹。
  • Windows / macOS:默认大小写不敏感,但保留大小写(Case-Insensitive, Case-Preserving)。在 Windows/Mac 眼里,layoutLayout 指向同一个地方。

当你在 Windows 或 Mac 上初始化一个 Git 仓库时,Git 为了入乡随俗,会自动把配置项 core.ignorecase 设置为 true(忽略大小写)。


避坑指南:为什么绝不推荐强制开启 Git 大小写敏感?

既然如此,我们直接把 Git 设置为“大小写敏感”不就行了?

# 千万别运行这行命令!
git config core.ignorecase false

千万不要这样做!

如果强行开启,由于操作系统和 Git 的认知产生了分歧,你会陷入“精神分裂”的灾难现场:

  1. 操作系统的视角layoutLayout 是同一个文件夹,硬盘上只有一个物理实体。
  2. Git 的视角(此时开启了敏感):Git 坚信这是两个完全不同的文件夹。
  3. 灾难发生:当你把文件夹改成 Layout 并提交后,Git 会认为你删除了旧的,新建了起初大写的
  4. 同事拉取代码:当同事拉取代码时,Git 会尝试在他的电脑上同时创建 layoutLayout。但 Windows/Mac 系统直接拒绝:“不行,已经有一个同名文件夹了!”
  5. 结果:同事的本地仓库直接卡死,报各种无法解决的幽灵冲突,分支切不过去,代码提不上去,直接宣告“社会性死亡”。

为什么即使设为敏感,目录和文件修改依然“无感”?

你可能会问:“好,我不改配置。但我明明在编辑器里把名字改了,为什么 Git 还是假装看不见?”

这里我们需要分“文件”“目录”两种情况来拆解底层的奇葩逻辑:

1. 针对文件(例如 utils.js ➡️ Utils.js

当你修改了文件名的大小写,Git 会去遍历它记录的文件索引,并询问操作系统:

Git:“系统老哥,我记录里的那个 utils.js 还在不在?内容变了没?”

系统(Mac/Win):“在的在的!好着呢,啥都没变!”(实际上系统看到的是 Utils.js,但由于系统不区分大小写,它觉得是一回事,于是对 Git 撒了谎

因为操作系统的这个“谎言”,Git 以为文件安然无恙,所以完全没有反应。

2. 针对目录(例如 layout ➡️ Layout

对于目录,情况更复杂。Git 的底层数据库里根本没有“目录”的概念,它只追踪文件路径

对于 Git 来说,并不存在一个叫做 layout 的“文件夹实体”,它只知道有一个文件,路径叫 app/components/layout/index.js

当你修改目录名为 Layout 时,Git 去问系统:“app/components/layout/index.js 在不在?”
系统同样会因为大小写不敏感而撒谎:“在的!”

因此,无论你改的是文件还是目录,在 Windows/Mac 上,操作系统都会帮着“瞒天过海”,导致 Git 彻底无感。


解决方案一:使用 git mv 强制重命名(命令行操作)

既然不能改配置,又不能指望系统自动检测,我们要怎么优雅地修改大小写呢?

git mv 相当于一个“越狱”命令,它不经过操作系统的询问,直接强行修改 Git 本身的索引缓存

💡 针对目录:稳妥的“黄金两步法”(中转法)

在 Windows/Mac 上,如果你直接运行 git mv app/components/layout app/components/Layout,系统通常会报错:Invalid argument,因为系统认为你在把“自己”重命名为“自己”。

所以,修改目录大小写,最稳妥的方法是引入一个临时中转名称

第一步:将目录重命名为一个临时名称

git mv app/components/layout app/components/layout_temp

这步之后,Git 会立刻识别到路径发生了变化。

第二步:将临时名称重命名为最终的大写名称

git mv app/components/layout_temp app/components/Layout

第三步:查看状态并提交

git status
# 输出:renamed:    app/components/layout/index.js -> app/components/Layout/index.js

最后正常提交并推送即可。


💡 针对文件:懒人技巧 —— 使用 -f 参数(一步到位)

如果你修改的是单个文件(例如 utils.js 改为 Utils.js),不需要像目录那样麻烦地用中转法,Git 提供了一个强制参数 -f(Force):

# 加上 -f 参数,强行覆盖 Git 索引
git mv -f utils.js Utils.js

运行后,Git 会立刻识别到文件的大小写变更,一步到位!

⚠️ 注意:这个 -f 快捷键只对文件有效。在很多 Windows/Mac 环境下,对目录使用 git mv -f 依然会报错,因此目录改名还是老老实实使用上面的“中转法”。

解决方案二:开启 TypeScript 强校验(工程化防错)

如果你觉得每次手动去命令行 git mv 还是太麻烦,或者担心团队里的其他小伙伴再次犯错,最根本的解决办法是在工程化层面进行拦截。

如果你的项目使用的是 TypeScript,你可以让 TS 编译器在本地帮你卡死这个问题。

在项目根目录的 tsconfig.json 中,开启以下配置:

{
  "compilerOptions": {
    // 强制要求文件名大小写一致
    "forceConsistentCasingInFileNames": true
  }
}

补充说明:官方的默认值是什么?

在 TypeScript 官方的编译选项说明中,forceConsistentCasingInFileNames默认值是 false(为了兼容一些历史老项目和在大小写敏感系统上的极端情况)。

但是!强烈建议你在所有新项目和现代项目中将其手动设置为 true

开启后的效果:
一旦你把硬盘上的文件夹改成了 Layout,但在代码中依然写着 import Layout from './layout'(小写),TypeScript 编译器在本地打包或热更新时就会直接红牌报错,并在编辑器里画红线,根本不给你提交“病态代码”的机会!


如果已经搞砸了(代码已乱),如何挽救?

如果你在看到这篇文章前,已经手动改了大小写,并且已经提交,导致本地和远程仓库处于一种“混乱且不同步”的状态,可以使用以下 “核武器级”清空重构法

  1. 备份你本地未提交的重要修改(防止丢失)。
  2. 清除 Git 本地缓存(这不会删除你硬盘上的物理文件):

    git rm -r --cached .
  3. 重新将所有文件加入暂存区(此时 Git 会按照当前硬盘上的实际大小写重新建立索引):

    git add .
  4. 提交并推送,瞬间还你一个干净、大小写对齐的健康仓库:

    git commit -m "fix: force reset git index case sensitivity"
    git push
分类: Git 笔记 标签: git大小写敏感

评论

暂无评论数据

暂无评论数据

目录