返回博客

Git 删除未跟踪文件:git clean 安全指南

2026年9月9日

TL;DR:删除 git 里未跟踪文件的命令是 git clean -fd——但永远先用 git clean -nd 预览,因为 clean 是永久删除,文件不会进回收站。 -f 删文件,-fd 连目录一起删,-fdx 把 gitignore 掉的构建产物也清掉。最高频的抱怨——“git clean 怎么删不掉我的未跟踪文件”——几乎总是两个原因之一:文件在某个未跟踪的目录里(补 -d),或者它们是被忽略的文件(补 -x)。未跟踪的杂物是实验、构建和各种随克隆而来的脚本的自然副产品;这篇指南讲清楚怎么预览每一次删除、怎么只清理单个目录,以及哪些参数组合在你珍惜的仓库里永远别跑。

git status 里的”未跟踪文件”是什么意思?

未跟踪(untracked)的意思是:git 在磁盘上看得见这个文件,但从没被告知要跟踪它——它不在索引里,也没有任何提交历史。git status 把所有东西分成三类:

$ git status --short
 M src/app.ts        # 已修改:已跟踪,有改动
?? notes.txt         # 未跟踪:git 不认识的新文件
?? build/            # 未跟踪目录:对 git 全新

这个区分很重要,因为每一类需要不同的清理工具。已跟踪但有改动的文件用 git restore 还原或直接提交掉——git clean 碰都不会碰它们。只有 ?? 开头的行才是 git clean 的辖区。被忽略的文件(所有被 .gitignore 匹配的)是隐藏的第四类:它们连 ?? 都不显示,git clean 默认跳过它们,除非你用 -x 显式选择加入。

如果你的真实问题是一个被跟踪却不该被提交的文件,清理是错误的工具——那是 git rm --cached 的活,或者按 git 撤销最后一次提交:保留改动 里讲的重置最后一次提交。

怎么删除 git 里的未跟踪文件?

核心命令是 git clean -f。不带 -f,git 拒绝删除任何东西,只打印一条警告——这是刻意设计的安全栏。完整流程如下:

# 1. 看清楚到底会删什么(dry run——什么都不会删)
git clean -nd

# 将会删除:
# notes.txt
# build/
# scratch/

# 2. 确认列表里没有宝贝,再真删
git clean -fd

逐个看参数:

  • -f / --force — 必需。真正删除未跟踪文件。
  • -d — 递归进入未跟踪的目录。光有 -f 只删除顶层的未跟踪文件,对目录只会报告”拒绝处理”。
  • -n / --dry-run — 显示本来会删什么。永远先跑这个。
  • -x — 连被忽略的文件一起删(node_modules、构建产物、.env)。
  • -X删被忽略的文件,保留未跟踪但未被忽略的。
  • -i — 交互模式;dry-run 列表很长时有用。

一个值得抄走的习惯:把 git clean -ndgit diff 用——提交前看一眼,清理前也看一眼。

为什么 git clean 删不掉我的未跟踪文件?

三个真实原因,按踩中的频率排序:

1. 文件在某个未跟踪的目录里。 只带 -f 时,git 删除散落的未跟踪文件,但遇到目录就停手,dry-run 里明明报告 Would remove build/,真跑起来却不动它。补上 -d:

git clean -fd

2. 文件被 gitignore 了。 node_modules/dist/.venv/——被忽略的路径对普通 clean 隐形。dry-run 不会列出它们,clean 也不会删它们。显式选择加入:

git clean -fdx   # 未跟踪 + 被忽略的文件和目录

3. 挡路的是嵌套 git 仓库或子模块。 git 从不从外面删另一个仓库的内容。要么正规地移除子模块,要么把 --force 给两次(git clean -ffd)——优先选前者。

如果 dry-run 什么都没列出来,git status 却还在显示 ??,你八成站错了工作树——跑一下 git rev-parse --show-toplevel,确认自己在想清理的那个仓库里。

怎么只删除某个目录里的未跟踪文件?

给 clean 传一个路径来限定范围——其余一概不碰:

git clean -fd build/          # 只删 build/ 里面的
git clean -fd src/generated   # 指定的一棵目录树

这就是”我想清掉 build/ 里的未跟踪文件和文件夹,但仓库根目录的草稿笔记要留着”的答案。路径相对于当前目录:在仓库根目录跑,作用范围是整个仓库;在子目录里跑,只作用于那棵子树。

能不能移除未跟踪文件但不真正删掉?

当 dry-run 列出了以后可能还想要的文件,别赌——先保全,再清理:

# 把未跟踪文件 stash 起来而不删除(-a 连被忽略的也一并收)
git stash push --include-untracked
git clean -fd                      # 工作树干净了
git stash pop                      # 需要时再取回来

git stash -u 把未跟踪文件移出工作树但保持可恢复——这才是人们真正想要的”移除而不删除”。想要一份可以留存的预览,git clean -nd > clean-plan.txt 会在你做任何决定之前给出精确清单。git clean -f 之后没有撤销:删了就是没了。

git clean、git rm、git restore:什么场景用哪个?

命令作用对象从磁盘删除适用场景
git clean -fd未跟踪的文件/目录删除 git 从未跟踪过的文件
git clean -fdx未跟踪 + 被忽略的彻底重置,连 node_modules、构建产物一起清
git rm <file>已跟踪文件是(进暂存区)删除文件让 git 记录这次删除
git rm --cached <file>已跟踪文件停止跟踪,文件留在磁盘上
git restore <file>已跟踪文件丢弃本地改动,保留文件

一句话规则:clean 管的是 git 不认识的东西;rm 和 restore 管的是它认识的。搞混这两者正是工作成果丢失的经典现场——以为 git clean -fdx 表现得像 git restore 就跑了下去。

哪些情况下绝对不要跑 git clean?

两条要直接戒掉的习惯:

  1. 永远不要在 monorepo 或 workspace 里盲跑 git clean -fdx 它会删掉所有被忽略的目录——每一个 node_modules、每一个 virtualenv、目录树里每一个本地 .env。找回来可能意味着一小时的重装,而被删掉的 .env 可能根本无法恢复。
  2. 永远不要把 force 固化进 alias。 git config alias.wipe "clean -fd" 感觉很高效,直到你把路径打错。让 dry-run 保持在一次按键的距离(git clean -nd),把”先预览再删除”变成雷打不动的两步仪式。

另外值得一提:在 [sync a fork with upstream] 拉取之前先清掉未跟踪文件,能缩小合并的冲突面——干净的工作树是最便宜的冲突保险:同步 fork 与上游,分步指南

安全 git clean 工作流,浓缩版

git status --short        # 工作树里有什么?
git clean -nd             # 预览:哪些*会*被删?
git clean -fd             # 删除未跟踪文件 + 目录
git clean -fdX            # (可选)只清被忽略的构建产物
git status --short        # 确认:工作树干净

预览、删除、验证——三十秒,零悔恨,git status 终于重新读起来是干净的了。

— mrsaynothing

Get the next one by email

One email per post. No spam, no algorithms.

self-hosted · no third parties · one-click unsubscribe

what is this?

Rsync vs SCP:Linux 复制命令怎么选

喜欢这些文章?我的本职工作就是这样的工程。 雇用我