把本地配置写进 .gitignore,改完文件却仍出现在 git status,甚至下一次提交又带上了它。这个场景通常不是 Git 缓存坏了,而是文件早已进入索引。下面区分“规则命中”和“停止跟踪”,给出保留本机文件的修复步骤与协作风险。
一、先分清:忽略规则不是文件删除器
Git 工作树是你正在编辑的文件,索引保存下一次提交准备记录的内容,提交历史保存已经形成的快照。.gitignore 主要约束尚未跟踪的路径;一个文件已经在索引里,后来补上规则,也不会自动退出索引。
规则写对和仍会提交可以同时成立。先问文件在哪一层,别对着 .gitignore 生闷气。本文只处理普通工作树单文件,子模块、稀疏检出和冲突路径另行评估。

二、用两组证据定位,不凭空白输出猜
先把当前改动另存到仓库外的安全位置,再进入仓库根目录。以下路径是示例,config/local.ini 只装普通本地选项,不放真实密码。先查索引,再查规则来源:
# 从仓库根目录检查,路径按实际项目替换
git rev-parse --show-toplevel
git status --short
git ls-files --error-unmatch -- config/local.ini
git check-ignore -v --no-index -- config/local.ini
git ls-files -ci --exclude-standard
ls-files --error-unmatch 找到该路径时返回 0;未找到时返回 1。check-ignore 默认不报告已跟踪文件,因此它没有输出,不一定是规则没写对。加 --no-index 才能暂时绕开索引判断,查看这条路径按忽略规则会如何处理。该参数不会修改索引,更不会停止跟踪。
-v 会显示规则文件、行号与模式。注意以 ! 开头的是反向规则,看到输出不等于“最终被忽略”;还要读模式含义。ls-files -ci --exclude-standard 列出索引中同时匹配标准忽略规则的路径,适合盘点已经跟踪却计划忽略的文件,不是自动清理清单。
三、只移除目标索引,保留自己的本地文件
在根部 .gitignore 加入 /config/local.ini,再准备无敏感值的 config/local.example.ini,供同事复制。先预览,只处理精确目标:
# 先编辑 .gitignore,加入 /config/local.ini
# 再准备无敏感值的 config/local.example.ini
git rm -n --cached -- config/local.ini
git rm --cached -- config/local.ini
git add -- .gitignore config/local.example.ini
git diff --cached --stat
git diff --cached --name-status
--cached 表示只从索引移除,本次操作保留当前工作树文件。普通 git rm 没有这个限定,会同时移除工作树文件,别少打一个参数。-- 用来分隔选项与路径;目录需要 -r,但本例是单文件,不需要扩宽作用范围。
操作被拒绝时先比较并保存 HEAD、索引和磁盘内容。--cached 要求索引与 HEAD 或工作树至少一方一致,别默认加 -f。暂存删除不等于本机文件消失;与新增示例相似时,差异也可能显示重命名。别把真实配置贴进工单。
四、同事更新代码前,先说明删除语义
“保留文件”只保证你这次 --cached 操作的当前工作树。把暂存删除提交后,其他克隆更新到这个提交时,原来被跟踪且没有本地修改的文件可能随更新被移除;有改动时也可能被更新操作拦住。不能把“我的文件还在”推广成“全团队文件永远还在”。
合并前通知同事备份并约定用 example 重新生成。本文停在暂存检查,不替你提交或推送。确认后再提交,说明停止跟踪本地配置,不要笼统写“清缓存”。
五、没跟踪却仍不忽略,查路径与父目录
如果索引里没有文件,就沿 check-ignore -v 的来源查。根部 /config/local.ini 只针对根目录的该路径;local.ini 可匹配更深层同名路径。子目录的 .gitignore 相对自身目录解释,且更近的规则文件可能覆盖上层规则。同一优先级内,最后一个匹配模式决定结果。
还有个常见陷阱:把父目录整个忽略后,单独给子文件加 !,通常不能重新纳入,因为 Git 不再遍历被排除的目录。若希望忽略根部 config 的直接内容,却保留示例文件,可以用下列规则;前提是没有其他有效规则继续排除父目录:
/config/*
!/config/local.example.ini
共享规则放 .gitignore;个人仓库规则放 .git/info/exclude;全局规则可用 core.excludesFile。“我这里正常”不等于团队策略落地,也别用 assume-unchanged 或 skip-worktree 冒充共享忽略机制。
六、失败分支:按症状找对应层
| 现象 | 优先确认 | 处理方向 |
|---|---|---|
| 规则命中仍显示修改 | 路径是否仍在索引 | 精确移除索引并提交规则 |
| check-ignore 没输出 | 是否已跟踪;路径是否正确 | 先 ls-files,再用 --no-index |
| !示例文件不生效 | 父目录是否被排除 | 允许遍历父目录,核对后续规则 |
| git rm --cached 被拒绝 | HEAD、索引、工作树差异 | 保存版本,不盲目强制 |
| 新提交不含配置,历史仍有 | 旧提交与其他副本 | 单独评估泄露和历史处置 |
不要为一个文件全仓库移除索引再添加,避免混入无关改动。git clean 是清理文件,不是修复忽略规则;允许清理忽略项的模式可能删掉刚保留的配置。
七、验收要同时看磁盘、索引和规则
# 文件仍在本机,但不再出现在索引中
test -f config/local.ini
git ls-files --error-unmatch -- config/local.ini
# 上一条预期退出 1;这是未跟踪,不是操作失败
git check-ignore -v -- config/local.ini
git status --short --ignored -- config/local.ini
git diff --cached --check
修复后应看到:磁盘文件存在;索引查询返回 1;规则检查指向有效的正向忽略模式;带 --ignored 的状态显示 !!。同时审核暂存差异,仅出现目标删除、忽略规则和安全示例,不夹带业务代码。diff --check 用于检查空白问题等,不是秘密扫描,也不是配置正确性的证明。
命令含预期非零分支,不宜直接塞进 set -e 脚本;应区分未跟踪、未忽略和命令错误。提交后还要在隔离克隆验证示例能生成可用配置。
八、实验范围与不能省掉的安全收尾
本文命令在隔离仓库用无敏感值 INI 执行:覆盖跟踪与忽略、父目录反向规则、索引移除后文件保留、暂存冲突拒绝及本地克隆更新行为。不修改生产仓库,不向远端推送。
如果历史提交曾含真实凭据,停止跟踪只是防止后续再次提交,不会清除历史、远端副本、缓存或他人的克隆。先撤销或轮换泄露凭据,再按协作与托管平台流程处理历史,并验证旧凭据失效;不要把一次 git rm 当成泄露事件结案。
参考资料:Git 官方 gitignore、git-rm、git-check-ignore、git-ls-files。本文命令在 Git 2.27.0 执行,不介绍新版本功能。
🔕 评论已关闭