晚上6点40分,你在错误的仓库窗口里敲下了git reset --hard HEAD~3。手指离开回车键的瞬间,大脑才完成处理——三天的代码,从工作区消失,用时不到一秒。
这种"先动手、后意识到"的窒息感,每个用Git的人都懂。但你可能不知道的是:Git并没有真的删掉那些提交。它们只是变成了"无引用"的悬空对象,躺在对象数据库里,等着垃圾回收来清理——而大多数仓库的垃圾回收频率,低到"最终"可能意味着几周。
![]()
reflog:Git的后悔药
救命的工具叫git reflog。它是一份本地日志,记录着HEAD指针最近的所有移动轨迹。关键点在于:reset操作本身也会被记进reflog,而不是抹掉之前的记录。
所以当你执行完reset,reflog里会同时存在两条记录——一条是"reset到HEAD~3",另一条是reset之前HEAD指向的那个提交哈希。找到那个哈希,执行git reset --hard 那个哈希,工作区就恢复到reset之前的状态,三个提交全部回来,用时比读完这句话还短。
reflog救不了的场景
但有个前提:这些提交必须真的存在过。如果你reset的是从未提交过的改动,reflog也无能为力——它只追踪HEAD和分支的指向,不记录从未进过版本库的文件内容。这种丢失是彻底的,唯一的防御手段是频繁提交,包括那些打算之后squash掉的临时"wip"提交,因为未提交的改动没有任何恢复路径。
reflog过期了怎么办
如果提交确实存在,但reflog条目已经过期——Git默认保留不可达的reflog条目90天,可达的更久——还有最后一招:git fsck --unreachable。这个命令能直接扫描对象数据库,找出悬空的提交对象。
执行git fsck --unreachable --no-reflog | grep commit,再用git show 哈希检查内容,确认是你要找的那个,然后reset回去。虽然麻烦,但总比重写代码强。
真正的教训:先演练,再动手
reflog救回了这次失误,但也教会我一件事:不能指望在压力下记得它存在。现在我的习惯是——任何真正有破坏性且不熟悉的操作,比如跨长历史的交互式rebase、filter-branch,或者任何带--hard或--force的命令,先在一次性克隆的仓库里跑一遍,确认它做的事和我想的一样,再对真实仓库执行。
具体做法很简单:用容器起一个一次性环境,克隆仓库,执行危险操作,检查结果,然后销毁。成本不到一分钟,但能避免在6点40分的疲惫状态下,用生产仓库做实验。
Git的恢复机制确实强大,但最好的恢复,是根本不需要恢复。
特别声明:以上内容(如有图片或视频亦包括在内)为自媒体平台“网易号”用户上传并发布,本平台仅提供信息存储服务。
Notice: The content above (including the pictures and videos if any) is uploaded and posted by a user of NetEase Hao, which is a social media platform and only provides information storage services.