「救命」的 Git 命令
2025-09-12 09:52:28 # Technical # Notes

前情提要

最近尝试 AI 编程工具 CladueCode 来做一个小项目,在 CladueCode 完成任务的最后,它还「好心」的为我提交 git,本着来都来了的心态,便让他提交了。但提交后就返现了不对,它将我本地暂存的文件都给提交了,这里有部分文件我是不打算提交或者不打算在这次提交的。于是我就开始用 git reset 进行回滚,于是乎重头戏来了。之前我要么用 git reset --soft 这样既能回滚 git 提交,也能将修改的文件回滚到暂存区保证不丢失,要么就是用 git reset --hard 直接回滚掉我确定不要了的提交。但这次,我看到了每次都一视而过的 git reset --keep 想着既然都叫 keep 了应该也是挺「稳妥」的,变尝试了,执行完回到暂存区一看,心率急升,暂存区啥都没了,说好的 keep 呢?

救命步骤

还好在 Reddit 看到类似的情况,然后底下有大佬推荐 git fsck --lost-foundgit reflog

git fsck --lost-found 看着原理太繁琐了,本来就已经心慌得不行,没法静下心来操作,于是打算先尝试 git reflog

直接执行 git reflog 查看历史所有操作的 SHA-1 值

git reflog

可以看到最新的一行就是我执行的 git reset --keep 将 HEAD 重置到 18f883f

然后第二行就是被弄丢的提交 bc8114e

现在要做的就是将 HEAD 重置到 bc8114e,这样理论上来说就可以恢复了

1
2
$ git reset bc8114e --hard
HEAD is now at bc8114e 完善异常处理系统:统一错误码体系和全局异常处理

YES,有惊无险💦,恢复了上次提交

查漏补缺

这里也是重新补补课,了解清楚 git reset 的四种模式

先初始化第一次提交,提交两个文件

first commit

然后在第二提交中修改 test_02.py 和新增一个 test_03.py

second commit

soft

执行 git reset --soft 0efa9be

git reset soft

可以看到 test_02.pytest_03.py 都回到了暂存区

mixed

执行 git reset --mixed 0efa9be

git reset mixed

可以看到 mixed 比 soft 更加彻底,不仅回滚提交,还将 test_03.py 会滚到了原本的工作区,而不是暂存区

hard

执行 git reset --hard 0efa9be

暂存区

可以看到暂存区和工作区都空了,完全回滚了到提交点状态

keep

重新修改 test_02.py 新增 test_03.py 并执行 git reset --keep 0efa9be

结果和上面的 hard 一致

hard 与 keep

那么 hard 和 keep 到底有什么区别呢?

这里在第二次提交之后,新增一个 test_04.py 到暂存区,然后执行 hard 与 keep 来看下差异

暂存 test_04

  • 先执行 git reset --keep

    工作区 test_04

    可以看到 test_04.py 回到了工作区

  • 重新执行第二次提交并将 test_04.py 添加回暂存区,然后执行 git reset --hard

    暂存区

    可以看到 test_04.py 也丢失了!

所以 hard 会清空暂存区,而 keep 不会!

git fsck

前面有提到,git fsck 也可以找回 git reset --hard 丢失的 commit,但是这个命令比较底层,并且有很多作用:

  • 检测对象是否损坏:Git 中 commit、tree、blob、tag 都是对象,git fsck 会检查这些对象的 SHA-1 校验和 是否正确,文件是否损坏
  • 检查引用关系:确认 commit 是否能正确引用到它的 parent、tree 等
  • 找出悬空对象(dangling objects):通常在 reset --hardrebaseamend 后,旧的 commit 就可能不被任何分支引用,这时这个提交就成为了「悬空」的,git fsck 会把这些孤立的对象列出来,这个就是找回丢失 commit 的关键
  • 清理仓库体积:通过 git fsck --lost-found 把悬空的对象移到 .git/lost-found 目录下,然后结合 git gc 可以彻底清理掉无用的对象,减少仓库体积

找回悬空提交

  1. 找出悬空的 commit

    1
    git fsck --lost-found
  2. 查看 commit 内容,确认是需要恢复的

    1
    git show <commit_hash>
  3. 建个新分支来保存这个 commit

    1
    git branch restore-branch <commit_hash>
  4. 将恢复的提交合并回主分支

    1
    2
    git checkout main
    git merge restore-branch

如果只是想把 commit 拿出来,不想新建分支,可以直接使用 cherry-pick

1
git cherry-pick <commit_hash>