Git撤销提交的代码怎么恢复

文章导读
大部分情况下能恢复,但恢复的对象是「曾经提交过的 commit」,不是你脑子里的那段代码。只要那次提交真的生成过 commit 对象,即使后来被 git reset --hard 从分支引用上摘掉,本地 git reflog 通常还能找到它的 SHA,把 SHA 重新挂到一个分支上就能拿回文件内容。反过来,如果代码从没 git add / git commit 过,或者改动被 git checko
📋 目录
  1. 一 撤销提交的代码:先把能核对的事实摆清楚
  2. 二 撤销提交的代码最容易搞错的一点
  3. 三 撤销提交的代码:一个照着就能做完的做法
  4. 四 撤销提交的代码:怎么验证结果对不对
A A

大部分情况下能恢复,但恢复的对象是「曾经提交过的 commit」,不是你脑子里的那段代码。只要那次提交真的生成过 commit 对象,即使后来被 git reset --hard 从分支引用上摘掉,本地 git reflog 通常还能找到它的 SHA,把 SHA 重新挂到一个分支上就能拿回文件内容。反过来,如果代码从没 git add / git commit 过,或者改动被 git checkout -- .、git restore 直接覆盖在工作区,reflog 里不会有记录,那时要找的是编辑器历史、IDE Local History 或 git stash list,而不是提交历史。所以先别急着再敲一次 reset,用一条命令确认对象还在不在。

判断方向:先用 git reflog 定位被撤销的 commit SHA,用 git cat-file -t <sha> 确认对象类型是 commit,再建一个救援分支指向该 SHA,最后用 cherry-pick 或 reset 选择性恢复。适用于被 reset、amend、rebase 掉的已提交内容;不适用于从未提交的工作区改动。边界:reflog 只存在于本地仓库,换机器、重新克隆、清理后不保证还在,恢复前先备份当前工作区。

撤销提交的代码:先把能核对的事实摆清楚

「撤销提交」在 Git 里对应好几种不同动作,代码的去向完全不同。先确认你执行的是哪一种,再决定去哪儿找,这一步比直接敲恢复命令更省时间。

执行过的操作代码当前的位置能核对的命令或文件
git reset --soft HEAD~1改动回到暂存区,文件仍在工作区git status 显示 Changes to be committed
git reset HEAD~1(默认 --mixed)改动回到工作区但未暂存git status 显示 Changes not staged for commit
git reset --hard HEAD~1工作区被重置,commit 对象仍在本地对象库git reflog、git fsck --lost-found
git commit --amend旧 commit 变成不可达对象git reflog、git fsck --unreachable
git revert <sha>原提交仍在历史里,只是多了一条反向提交git log --oneline 能看到两条记录
git checkout -- <file>文件回到 HEAD 版本,未提交改动丢失只能查编辑器历史或 git stash list

几个固定可以核对的位置:reflog 的实际内容是 .git/logs/HEAD(当前分支另有 .git/logs/refs/heads/<branch>),git reflog 读的就是它;git fsck --lost-found 会把悬空对象写到 .git/lost-found/commit/ 下,文件名就是 SHA;git fsck --unreachable 则直接在终端列出不可达对象。reflog 的保留期和 gc.reflogExpire 有关,用 git config --get gc.reflogExpire 查,没有输出说明走默认值,具体默认值以 git help config 里 gc.reflogExpire 和 gc.reflogExpireUnreachable 条目为准。

撤销提交的代码最容易搞错的一点

最常见的误解是把 git reset --hard 当成「删除提交」。它移动的是分支引用,commit 对象并不会立刻消失,要等对象回收流程跑过、且超过保留期才可能被清理。所以刚撤销完就去 reflog 找,大概率还在。真正需要判断的情况是:这个 SHA 是否还存在于本地对象库。

Git撤销提交的代码怎么恢复

能把两种情况区分开的检查动作只有两步,在仓库根目录执行:

git reflog --date=iso -n 30
git cat-file -t <你怀疑的SHA>

判断分界:reflog 里能看到 <sha> HEAD@{n}: commit: ... 这样一行,并且 git cat-file -t 输出 commit,说明对象在,走恢复流程;如果 cat-file 报 fatal: Not a valid object name,或 reflog 里根本没有这条记录,就别继续在 reflog 上耗时间,改查 git fsck --lost-found、git stash list、IDE 的 Local History。

还有一个方向相反的误判:不带 --hard 的 git reset 之后,文件其实还完整留在工作区,git status 一看就明白。这种情况如果又去 git reset --hard HEAD@{1},反而会把当前未提交的改动冲掉,把本来没丢的东西弄丢。

撤销提交的代码:一个照着就能做完的做法

下面这条路径按「先冻结现场、再定位、再挂分支、最后选择性恢复」的顺序走,每一步都写明该看到什么。把示例里的 SHA 换成你自己 reflog 里看到的那个。

Git撤销提交的代码怎么恢复
# 0. 冻结现场:当前未提交的改动先存起来,防止后面 reset 冲掉
git status
git stash push -u -m "before-recover"

# 1. 定位被撤销的提交
git reflog --date=iso -n 30

该看到什么:一行行形如 a1b2c3d HEAD@{3}: reset: moving to HEAD~1 的记录。撤销动作那一行的上一行,通常就是被摘掉的 commit,记下它的 SHA。

# 2. 确认对象还在(把 a1b2c3d 换成上一步看到的 SHA)
git cat-file -t a1b2c3d
# 期望输出:commit

# 3. 建救援分支,不移动当前分支,比直接 reset 安全
git branch rescue a1b2c3d
git log --oneline --stat -n 3 rescue

该看到什么:git log 打印出那条提交的信息和改动文件列表,和记忆中的提交对得上再继续。

# 4a. 只想把那次提交的改动拿回当前分支
git cherry-pick a1b2c3d

# 4b. 或者让当前分支整体回到那个状态(会丢弃该提交之后的记录,确认后用)
git reset --hard a1b2c3d

走 4a 后该看到新生成一条提交、git status 干净;如果提示冲突,git status 会列出 both modified 的文件,按正常冲突处理,再 git cherry-pick --continue 或 git cherry-pick --abort。走 4b 后该看到 HEAD 直接指向那个 SHA。

如果第 1 步 reflog 里没有,改走悬空对象:

Git撤销提交的代码怎么恢复
git fsck --lost-found
ls .git/lost-found/commit | head
git show <SHA> --stat

确认文件内容对得上之后,同样用 git branch rescue <SHA> 把它挂住。需要让远端也留一份就 git push origin rescue;不建议用 force-push 主分支来「恢复」,会影响其他人的工作副本。最后如果第 0 步存过改动,用 git stash pop 拿回来。

撤销提交的代码:怎么验证结果对不对

验证不能只看 git log 里有没有那条 commit,要落到文件内容和差异上。按顺序做这几条:

  • git log --oneline -5:恢复的提交出现在预期位置,SHA 与 reflog 里记下的一致。
  • git diff <被恢复的SHA> HEAD --stat:如果是 reset --hard 回到该提交,应无输出;如果走 cherry-pick,应只出现该提交之后你自己的新增内容。
  • git show <SHA>:path/to/file:直接看具体文件内容,路径相对仓库根,确认关键函数或配置行确实回来了。
  • git status:正常结果是 working tree clean;如果之前 stash 过,git stash list 里对应的条目应已消失。

页面位置上的核对:GitHub、GitLab 的 Branches 页面能看到新建的 rescue 分支,进入该分支的 commit 页面能看到完整 diff。如果仓库配置了流水线,推送 rescue 分支会触发一次构建,用它跑一遍编译或测试,比人眼比对更可靠。

正常结果长什么样:git diff 无输出或只有你预期的差异,git status 干净,目标文件内容与撤销前一致,git fsck 不报 error。若 git status 里仍有大量未跟踪文件,或者 git diff 出现你没预期的删除,先别继续提交,回到第 2 步重新确认 SHA 选对了没有。