简介
命令格式:
shell
git rm --cached <file>
意思:
从
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) |
当执行:
shell
git add file.txt
文件被添加到 index
(暂存区)。
当执行:
shell
git commit
暂存区内容被保存为新的 commit
(快照)。
而:
shell
git rm --cached file.txt
是从暂存区删除这个文件的记录(同时停止跟踪),但不删除工作区文件。
效果演示
假设当前有一个文件:
shell
$ echo "test" > test.log
$ git add test.log
$ git commit -m "add log file"
现在 test.log
已经被 Git
跟踪。
如果加了 .gitignore
:
shell
*.log
这时 .gitignore
对 test.log
不起作用,因为它已经在仓库历史中被跟踪了。
解决办法:
shell
git rm --cached test.log
执行结果:
shell
rm 'test.log'
然后执行:
shell
git status
会显示:
shell
deleted: test.log
Git
认为你要"从版本控制中删除它",但文件还在硬盘上。
接着提交一次:
shell
git commit -m "Stop tracking test.log"
最终效果:
-
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 跟踪中移除(保留物理文件) |
查看哪些文件仍在索引中
可以用以下命令查看:
shell
git ls-files
移除缓存前:
shell
test.log
执行 git rm --cached test.log
后:
shell
(test.log 不再出现)
高级用法
批量移除已跟踪但应忽略的文件
shell
# 查看所有已跟踪的文件
git ls-files
# 结合 grep 找到特定模式的文件并移除
git ls-files | grep '\.tmp$' | xargs git rm --cached
# 或者使用 find 命令
find . -name "*.log" -exec git rm --cached {} \;
从所有提交历史中完全删除文件
如果文件包含敏感信息且已经推送到远程,需要从历史中完全删除:
shell
# 1. 使用 filter-branch 从所有提交中删除文件
git filter-branch --force --index-filter \
'git rm --cached --ignore-unmatch secrets.txt' \
--prune-empty --tag-name-filter cat -- --all
# 2. 强制推送到远程
git push origin --force --all
实际场景案例
迁移到大文件存储 (Git LFS)
shell
# 发现大文件已被普通 Git 跟踪
git ls-files | grep -E '\.(psd|ai|zip)$'
# 从普通 Git 跟踪中移除
git rm --cached design.psd large-video.mp4
# 配置 Git LFS
git lfs track "*.psd" "*.mp4"
# 重新添加文件(现在通过 LFS 跟踪)
git add design.psd large-video.mp4
git commit -m "Migrate large files to LFS"
清理误提交的依赖目录
shell
# 误提交了 node_modules
git add . # 不小心包含了 node_modules
# 从 Git 中移除
git rm --cached -r node_modules/
# 确保 .gitignore 包含 node_modules
echo "node_modules/" >> .gitignore
# 提交
git add .gitignore
git commit -m "Remove node_modules from version control"
分离环境配置文件
shell
# 开发环境配置文件不应提交
git rm --cached config/dev.json
# 创建模板文件供其他开发者使用
cp config/dev.json config/dev.json.template
git add config/dev.json.template
echo "config/dev.json" >> .gitignore
git add .gitignore
git commit -m "Make dev config local only"
注意事项
-
不会删除物理文件(只改
Git
索引); -
必须提交一次,远程仓库才会真正删除它;
-
如果多人协作,建议在
.gitignore
里同步写入规则,防止其他人再次添加; -
想恢复跟踪,只需:
shell
git add <file>
git commit -m "track file again"
底层原理
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
是其逆操作,从索引移除。