我在云原生环境里折腾过一阵子,遇到“代码改乱了、CI 挂了、本地环境搞崩了”这类情况时,git checkout -- . 是我敲得最频繁的命令之一。但很多人第一次看到它都会懵:后面那个 -- 是什么意思?那个点又代表什么?它和 git checkout 切分支有什么区别?更尴尬的是,有次我看到同事在容器里执行完这条命令后一脸绝望——“我改了一整天的代码怎么没了?”
这篇文章就围绕这个命令串起一整条知识线。我会先讲清楚 git checkout -- . 的确切含义,再结合云原生场景说说为什么它这么常用,然后重点解答那个热搜问题:git checkout problem 如何选择——到底什么时候用 checkout、什么时候用 restore、什么时候用 reset,避免你手一抖把不该丢的东西丢了。
1. 先搞清楚:git checkout -- . 到底做了什么
1.1 从 Git 的“三个区域”说起
要理解这个命令,你得先在心里建立 Git 最基本的模型:工作区(Working Directory)、暂存区(Staging Area / Index)、版本库(HEAD / Repository)。
我用一个生活化的类比来解释。假设你在写一份纸质报告:
- 工作区就是你桌面上摊开的草稿纸,你随手乱画、写错字、贴便签,都发生在这一层;
- 暂存区像是一个“待提交的文件夹”,你把满意的几页挑出来放进去,准备统一复印;
- 版本库就是公司档案室的存档柜,每次复印装订好一份,就代表一个历史版本。
git checkout -- . 做的事情,用这个类比说就是:把桌面上所有草稿纸,强制替换成“待提交文件夹”里的那份内容。你在草稿纸上的随手涂鸦、写错的部分、后来的修改,全部丢弃。
换句话说,这条命令的作用是:用暂存区的内容覆盖工作区当前内容,从而丢弃所有尚未暂存的修改。注意关键词是“尚未暂存”。
1.2 逐词拆解:checkout、--、. 分别是什么
我们把这个命令拆成三块来看:
git checkout:这是 Git 的一个历史悠久的命令,它身兼两职。第一个职责是切换分支,比如git checkout main;第二个职责是恢复工作区文件,比如git checkout -- README.md。我们现在讨论的是它的第二个职责。--:这是 Git 参数解析里的“分隔符”,含义是“后面跟的都是路径,不是分支名或 commit”。没有这个分隔符,git checkout main里的main会被当成分支名;加了--之后,git checkout -- main就是“把名为 main 的文件恢复成暂存区版本”,而不是切换到 main 分支。.:这是一个路径,代表“当前目录”。在 Git 路径语法里,.表示当前所在目录,git checkout -- .就等于“当前目录下所有文件(递归)”。
所以连起来理解,git checkout -- . = “把当前目录下所有文件,恢复成暂存区里的状态”。如果当前在仓库根目录执行,就等同于“把整个工作区所有已跟踪的文件恢复成暂存区状态”。
1.3 哪些东西会被丢,哪些不会丢
这一节非常重要。很多人在执行这条命令之前没搞明白影响范围,结果骂 Git 是“数据粉碎机”。我直接给你一个对照表:
| 文件状态 | 执行 git checkout -- . 后的结果 |
|---|---|
已跟踪文件,修改了但未 git add |
修改被丢弃,文件恢复为暂存区版本 |
已跟踪文件,修改后执行过 git add |
文件保持不变,因为暂存区就是最新版本 |
已跟踪文件,git add 后又改了,且第二次修改未 add |
文件恢复成“暂存区版本”,第二次修改丢弃 |
| 未跟踪的新文件(从未 add 过) | 不受影响,文件还在 |
| 被删除的已跟踪文件 | 文件会被恢复回来(因为暂存区里还有记录) |
被 .gitignore 忽略的文件 |
完全不受影响 |
看到没有,这个命令 不是“清空所有改动”,它只影响“已跟踪且未暂存”的那部分改动。这也是很多人第一次困惑的根源:执行完 git checkout -- . 后,发现左边怎么还有文件?那多半是新建的、从未 git add 过的文件,或者是被忽略的配置文件。
1.4 与 git checkout HEAD -- . 的区别
有一个很相近的命令叫 git checkout HEAD -- .,它和 git checkout -- . 只有一字之差,但行为有本质不同:
git checkout -- .:用暂存区覆盖工作区;git checkout HEAD -- .:用 HEAD(最近一次提交)同时覆盖暂存区和工作区。
换句话说,前者只丢“没 add 的改动”,后者把你“已经 add 还没来得及提交的改动”也一起丢了,相当于让当前目录完全回到上次提交的状态。
实际使用中,我见过两种习惯。有人喜欢用 git checkout HEAD -- . 图一个“
