惊魂Git reset hard时刻

今天在同事电脑上操作分支提交,因为拉不下来有冲突,想要把当前的内容备份然后创建一个新分支,然后再切回来回退到之前的提交,再操作就行。

结果会退回来后,发现本地刚刚备份的分支的提交也被清空了,何で?不会因为没有推送到远端,本地直接都全部影响删除了吧?基本上就要放弃,以为 reset hard 是真的删除了。就随手搜了下有没有方法恢复,结果还真有,记录下。

一句话总结

当前的提交记录如下:


(0) 初始状态:

main: C1 ---- C2 ---- C3 (HEAD)

   C1 ---- C2 ---- C3
                    ^
                   HEAD
                   main

-------------------------------------------------------------

(1) 执行 "git reset --hard C1" 后:

   C1 ---- C2 ---- C3
   ^
   HEAD
   main

(这里 C2, C3 没有被删除,只是没有分支引用)

-------------------------------------------------------------

(2) 查看 reflog:

   HEAD@{0}: reset: moving to C1
   HEAD@{1}: commit: ...
   HEAD@{2}: commit: ...
   ...

(在 HEAD@{1} 处可以看到指向 C3 的记录)

-------------------------------------------------------------

(3) 执行 "git reset --hard <hash_of_C3>" 恢复到 C3:

   C1 ---- C2 ---- C3
                    ^
                    HEAD
                    main

恢复完成!

o1

当我们执行了 git reset --hard 之后,Git 会将工作区和暂存区都回退到指定的提交,同时丢弃了在此之后的所有内容。如果不小心回退错了,想要找回原本的提交或文件修改,可以尝试以下几种方法:


1. 使用 Git Reflog 找回丢失的提交

什么是 Reflog?

  • Reflog(“Reference log”)是 Git 记录所有 “引用(ref)” 的移动历史的地方。
  • 每次提交、合并、reset、rebase 等操作,Git 都会将旧的 HEAD、分支指针的变动记录到 reflog 中。
  • 借助 reflog,我们通常可以找到在本地“丢失”的提交。

如何使用?

  1. 查看 reflog

    git reflog

    这条命令会列出你最近所有的提交移动历史(包括 HEAD 的变动)。列表中会显示每次提交或操作对应的哈希值(commit id),以及时间、操作类型等。

  2. 找到需要恢复的提交
    在 reflog 列出的条目中找到你想要恢复的那一次提交(或者 HEAD 所指向的哈希值)。

  3. 执行回退或检出

    • 如果你想让当前分支回到那个提交,可以:
      git reset --hard <commit_id>
      这样就会将当前分支强制移动到该提交,并且工作区会变成该提交对应的状态。
    • 如果你只是想查看或者临时取出那个提交中的文件,又不想把当前分支的 HEAD 移过去,可以使用:
      git checkout <commit_id>
      这样会切换到一个“分离(detached)HEAD”状态,只是让你的工作区看起来是那个提交的内容。

注意:如果你进行了“重新提交”之类的操作,一定要确认好现在 HEAD 的状态,避免在错误的 commit 上继续修改。


2. 如果在 reset --hard 前并没有提交过

  1. 如果代码从来没有提交过(处于工作区和暂存区),执行了 git reset --hard 后,基本无法直接恢复
  2. 这种情况下只能尝试:
    • 在本地文件系统做搜索:看看有没有在其他分支、其他文件夹、临时备份地方留存的副本。
    • 如果之前曾经用编辑器或 IDE 自动保存过可能的备份,可以到相应的自动保存或历史记录功能里找找。

3. 如果使用了 Stash

如果在 reset --hard 之前你有使用过 git stash 临时保存工作区的修改,那么可以通过 git stash list 来查看你的 stash 列表。随后使用:

git stash apply

git stash pop

来恢复 stash 中的修改。

但如果从来没执行过 git stash,那就不会有对应记录。


4. 如果远程仓库有过提交

若你已经将本地修改过的内容推送到了远程仓库,比如 origin,那么这些提交在远程还是存在的。可以尝试切换到远程分支或直接拉取远程分支上对应的提交。


小结

  • 最常用、最有效的方式就是依靠 Git 的 reflog 来恢复“丢失”的提交。
  • 一旦做过提交,无论是误操作的 reset --hardrebase 还是其他操作,往往都能在 reflog 中找回之前的提交记录。
  • 如果从头到尾都没有提交过,只是一些暂存/未暂存的修改,那么执行 reset --hard 确实会导致本地修改被清空,几乎无法通过 Git 再恢复。只能去找代码编辑器的自动备份、或者系统文件级别的恢复。

操作示例

假设你的 reflog 中出现类似下面的历史:

d3456f7 HEAD@{0}: reset: moving to HEAD~1
f1abc23 HEAD@{1}: commit: [你的提交信息]
acbd123 HEAD@{2}: commit: ...
...

如果你想回到 f1abc23 这个提交,只需执行:

git reset --hard f1abc23

这样就可以恢复到执行 reset --hard 之前的状态。

祝你操作顺利,也记得养成随时提交使用分支的好习惯,以减少误操作带来的损失。

参考

Git reset --hard 后如何恢复?