简介
命令格式:
1git rm --cached <file> 2
意思:
从
Git的 索引(index,暂存区) 中移除文件,但保留工作区中的实际文件。
也就是说:
- 文件仍然留在硬盘(工作区);
- 但不再被
Git跟踪(tracked)。
<file>...:要移除的文件或目录路径。可以指定多个文件,或使用通配符(如 *.log)。 常用选项:
--cached:仅从索引移除(必须使用)。-r或--recursive:递归移除目录及其内容(如果指定目录)。-f或--force:强制移除,即使暂存内容不匹配分支tip或磁盘文件。-q或--quiet:安静模式,抑制输出。--ignore-unmatch:如果文件不在索引中,继续执行而不报错。
三大区域
Git 有三层结构:
| 层级 | 名称 | 说明 |
|---|---|---|
| Working Directory | 工作区 | 真实存在的文件 |
| Index (Staging Area) | 暂存区 | 下次提交的快照 |
| Repository | 仓库 | 提交的历史记录(.git/objects) |
当执行:
1git add file.txt 2
文件被添加到 index(暂存区)。
当执行:
1git commit 2
暂存区内容被保存为新的 commit(快照)。
而:
1git rm --cached file.txt 2
是从暂存区删除这个文件的记录(同时停止跟踪),但不删除工作区文件。
效果演示
假设当前有一个文件:
1$ echo "test" > test.log 2$ git add test.log 3$ git commit -m "add log file" 4
现在 test.log 已经被 Git 跟踪。
如果加了 .gitignore:
1*.log 2
这时 .gitignore 对 test.log 不起作用,因为它已经在仓库历史中被跟踪了。
解决办法:
1git rm --cached test.log 2
执行结果:
1rm 'test.log' 2
然后执行:
1git status 2
会显示:
1deleted: test.log 2
Git 认为你要“从版本控制中删除它”,但文件还在硬盘上。
接着提交一次:
1git commit -m "Stop tracking test.log" 2
最终效果:
test.log在仓库中被移除(不再跟踪)- 本地文件还在(不会被删除)
.gitignore会开始对它生效(以后不会再被加入)
常见用法场景
| 场景 | 命令 | 说明 |
|---|---|---|
| 让 .gitignore 生效(停止跟踪) | git rm --cached <file> | 典型用途 |
| 停止跟踪整个目录 | git rm -r --cached <dir> | 递归地移除目录 |
| 停止跟踪所有已跟踪但应忽略的文件 | git rm -r --cached . + git add . | 重置索引(慎用) |
| 移除缓存但不删物理文件 | git rm --cached | 保留文件在工作区 |
与 git rm(不带 --cached)的区别
| 命令 | 移除暂存区 | 删除工作区文件 | 说明 |
|---|---|---|---|
| git rm file | ✅ | ✅ | 删除文件并记录提交(彻底删) |
| git rm --cached file | ✅ | ❌ | 仅从 Git 跟踪中移除(保留物理文件) |
查看哪些文件仍在索引中
可以用以下命令查看:
1git ls-files 2
移除缓存前:
1test.log 2
执行 git rm --cached test.log 后:
1(test.log 不再出现) 2
高级用法
批量移除已跟踪但应忽略的文件
1# 查看所有已跟踪的文件 2git ls-files 3 4# 结合 grep 找到特定模式的文件并移除 5git ls-files | grep '\.tmp$' | xargs git rm --cached 6 7# 或者使用 find 命令 8find . -name "*.log" -exec git rm --cached {} \; 9
从所有提交历史中完全删除文件
如果文件包含敏感信息且已经推送到远程,需要从历史中完全删除:
1# 1. 使用 filter-branch 从所有提交中删除文件 2git filter-branch --force --index-filter \ 3 'git rm --cached --ignore-unmatch secrets.txt' \ 4 --prune-empty --tag-name-filter cat -- --all 5 6# 2. 强制推送到远程 7git push origin --force --all 8
实际场景案例
迁移到大文件存储 (Git LFS)
1# 发现大文件已被普通 Git 跟踪 2git ls-files | grep -E '\.(psd|ai|zip)$' 3 4# 从普通 Git 跟踪中移除 5git rm --cached design.psd large-video.mp4 6 7# 配置 Git LFS 8git lfs track "*.psd" "*.mp4" 9 10# 重新添加文件(现在通过 LFS 跟踪) 11git add design.psd large-video.mp4 12git commit -m "Migrate large files to LFS" 13
清理误提交的依赖目录
1# 误提交了 node_modules 2git add . # 不小心包含了 node_modules 3 4# 从 Git 中移除 5git rm --cached -r node_modules/ 6 7# 确保 .gitignore 包含 node_modules 8echo "node_modules/" >> .gitignore 9 10# 提交 11git add .gitignore 12git commit -m "Remove node_modules from version control" 13
分离环境配置文件
1# 开发环境配置文件不应提交 2git rm --cached config/dev.json 3 4# 创建模板文件供其他开发者使用 5cp config/dev.json config/dev.json.template 6git add config/dev.json.template 7 8echo "config/dev.json" >> .gitignore 9git add .gitignore 10 11git commit -m "Make dev config local only" 12
注意事项
- 不会删除物理文件(只改
Git索引); - 必须提交一次,远程仓库才会真正删除它;
- 如果多人协作,建议在
.gitignore里同步写入规则,防止其他人再次添加; - 想恢复跟踪,只需:
1git add <file> 2git commit -m "track file again" 3
底层原理
git rm --cached 的工作机制涉及 Git 的核心数据结构:
索引(Index/Staging Area):
Git的索引是一个二进制文件(.git/index),记录暂存的文件状态(内容哈希、模式、路径)。--cached只修改索引,而不触及工作目录或对象数据库(objects)。
安全检查:
Git要求暂存内容与分支tip(最新提交的树对象)或磁盘文件匹配。这防止你意外移除有本地修改的文件。- 如果不匹配,
Git报错:error: path 'file.txt' is unmerged或did not match any files。 - 使用
-f绕过此检查。
对象影响:
- 移除后,下次提交的树对象(
tree object)将不包含该文件。 - 历史记录不受影响:旧提交中仍保留文件(通过
Blob对象)。 - 与快照机制相关:
Git存储完整快照,但移除只影响未来快照。
与 git add 的反向:
git add将工作目录文件添加到索引。git rm --cached是其逆操作,从索引移除。
