惊魂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,我们通常可以找到在本地“丢失”的提交。
如何使用?
-
查看 reflog
git reflog
这条命令会列出你最近所有的提交移动历史(包括
HEAD
的变动)。列表中会显示每次提交或操作对应的哈希值(commit id),以及时间、操作类型等。 -
找到需要恢复的提交
在 reflog 列出的条目中找到你想要恢复的那一次提交(或者 HEAD 所指向的哈希值)。 -
执行回退或检出
- 如果你想让当前分支回到那个提交,可以:
这样就会将当前分支强制移动到该提交,并且工作区会变成该提交对应的状态。git reset --hard <commit_id>
- 如果你只是想查看或者临时取出那个提交中的文件,又不想把当前分支的 HEAD 移过去,可以使用:
这样会切换到一个“分离(detached)HEAD”状态,只是让你的工作区看起来是那个提交的内容。git checkout <commit_id>
- 如果你想让当前分支回到那个提交,可以:
注意:如果你进行了“重新提交”之类的操作,一定要确认好现在 HEAD 的状态,避免在错误的 commit 上继续修改。
2. 如果在 reset --hard
前并没有提交过
- 如果代码从来没有提交过(处于工作区和暂存区),执行了
git reset --hard
后,基本无法直接恢复。 - 这种情况下只能尝试:
- 在本地文件系统做搜索:看看有没有在其他分支、其他文件夹、临时备份地方留存的副本。
- 如果之前曾经用编辑器或 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 --hard
、rebase
还是其他操作,往往都能在 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
之前的状态。
祝你操作顺利,也记得养成随时提交和使用分支的好习惯,以减少误操作带来的损失。