Git 的五个"后悔药":什么时候用 restore、reset、revert、reflog
删错了文件、提交错了分支、把别人的改动覆盖了——这些事谁都干过。Git 的好处是几乎每一步都能退回去,坏处是"怎么退"有五六种写法,选错了会把事情变得更糟。
这篇把五个常用的恢复手段理清楚,顺便说清哪个是真正危险的。
一、git restore:撤销工作区的修改
最常见的需求:改了几个文件,发现方向不对,想全部丢掉。
# 丢弃工作区里某个文件的修改
git restore path/to/file.js
# 丢弃所有未暂存的修改
git restore .
# 取消暂存(把文件从暂存区拿出来,但保留修改)
git restore --staged path/to/file.js
注意最后一个:--staged 只动暂存区,文件内容不变。很多人习惯用 git reset HEAD 做同样的事,效果一样,但 restore 语义更清楚——它只管恢复,不牵扯提交历史。
危险等级:中。 丢弃的修改没有进过 Git,找不回来。执行前先 git status 看一眼,或者干脆 git stash 存一下。
二、git reset:撤销提交,三种模式差别很大
这是最容易用错的一个。三个参数的行为完全不同:
| 参数 | 提交历史 | 暂存区 | 工作区 |
|---|---|---|---|
| --soft | 回退 | 保留 | 保留 |
| --mixed(默认) | 回退 | 清空 | 保留 |
| --hard | 回退 | 清空 | 清空 |
# 撤销最近一次提交,改动仍在暂存区,可以直接重新提交
git reset --soft HEAD~1
# 撤销提交并取消暂存,改动还在工作区,可以重新挑选要提交的部分
git reset HEAD~1
# 彻底丢弃这次提交及其所有改动
git reset --hard HEAD~1
日常用得最多的是 --soft 和默认的 --mixed:提交信息写错了、少加了文件、提交粒度太粗,都靠它们重来。
危险等级:--hard 是真正危险的。 它会连工作区的改动一起抹掉,而且这些改动从来没被 Git 记录过。用之前一定确认:这次的改动里有没有还没提交、也没别处备份的东西。
还有一点:reset 会改写历史。如果是已经推到远端的提交,reset 之后必须 push --force,而 force push 会影响所有基于这些提交工作的人。团队协作时这一条几乎是红线。
三、git revert:安全的"反向提交"
如果提交已经推到共享分支,正确做法不是 reset,而是 revert:
# 生成一个新提交,内容正好抵消指定提交的改动
git revert <commit-hash>
它不删除历史,只是在末尾追加一次反向操作。历史保持完整,别人拉代码不会冲突。
代价是历史里会留下"提交—撤销"两条记录。对于已经公开的提交,这个代价是值得付的。
判断标准很简单:这个提交别人拉过没有? 拉过就用 revert,没拉过才考虑 reset。
四、git reflog:找回"已经消失"的提交
reflog 记录的是本地 HEAD 的每一次移动,包括那些已经被 reset 掉的提交。它是撤销操作的最后一道保险。
# 看最近的操作记录
git reflog
# 输出大致长这样
# a1b2c3d HEAD@{0}: reset: moving to HEAD~1
# e4f5g6h HEAD@{1}: commit: 添加用户导出功能
只要找到那一行,就能把提交捡回来:
git reset --hard e4f5g6h
# 或者
git cherry-pick e4f5g6h
reflog 默认保留 90 天(未引用的对象 30 天)。也就是说,误删的提交在大多数情况下都还在,只是不在任何分支上,普通 git log 看不见。
这是我个人觉得最值得记住的一条:手滑 reset --hard 之后,先别慌,先 git reflog。
五、git stash:临时挪开改动
改到一半要去修个紧急 bug,但当前改动还不适合提交:
# 存起来(含未跟踪文件要加 -u)
git stash push -u -m "重构到一半"
# 看存了哪些
git stash list
# 恢复最近一次(并从栈里删除)
git stash pop
# 恢复但保留在栈里
git stash apply
stash 的坑在于它很容易被遗忘。存了五六个栈,过一周完全不记得哪个是哪个。所以养成带 -m 写说明的习惯,比事后翻 diff 强得多。
危险等级:低,但容易"存了等于丢了"。 stash 里的东西不在任何分支上,git log 看不到。不过它同样能被 reflog 找回(git fsck --unreachable 也能捞)。
六、一张判断表
真出问题时,按这个顺序判断:
| 你想做什么 | 用什么 | 危险点 |
|---|---|---|
| 丢掉工作区修改 | git restore . | 改动不可恢复 |
| 撤销提交、保留改动 | git reset --soft HEAD~1 | 安全 |
| 彻底丢弃提交和改动 | git reset --hard | 高危,先 git status |
| 撤销已公开的提交 | git revert | 安全,历史留痕 |
| 找回被 reset 的提交 | git reflog | 90 天内大概率还能救 |
| 临时挪开改动 | git stash -u -m "说明" | 记得写说明 |
七、小结
三句话能概括:
没提交过的改动,只有 stash 和备份能救;提交过的改动,reflog 几乎总能救;已经公开的提交,只能用 revert 而不能 reset。
真正需要小心的只有 reset --hard 和 push --force 这两个。其余的操作,只要提交进过仓库,基本都有退路。
养成一个习惯就好:做任何"可能删东西"的操作之前,先跑一次 git status,再看一眼 git log --oneline -5。 十秒钟的成本,能省掉一次深夜的数据恢复。
文中命令基于 Git 2.x 实测。
- 点赞
- 收藏
- 关注作者
评论(0)