[toc]
概述:
git pull拉取远程后,本地某个项目的源码被”弄丢”了,工作区只剩bin/、obj/编译产物。本文记录如何通过 reflog + 孤立提交(dangling commit) 找回源码并重新纳入版本控制。
场景还原
某个仓库(此处为 .Net-Code,内含 FunctionPointerDemo、HarmonyDemo 等项目)在 git pull 后:
- 两个项目的源码文件(Program.cs、csproj、README.md 等)消失,目录里只剩
bin/和obj/(编译产物)。 git status却没有显示这两个目录被删除。
为什么 git status 不显示删除?
因为这两个项目所在的 commit,在 pull 的 fast-forward 合并后脱离了 main 分支的历史,变成了”孤立提交(dangling commit)“。它们的文件对象仍然完整保存在 .git 对象库里,只是没有任何分支引用它们了。
恢复五步法
第 1 步:确认是 submodule / gitfile 结构
.git 是个普通文件(不是目录)时,说明是 submodule:
cat .git
# gitdir: ../.git/modules/.Net-Code这能帮你理解仓库的 git 数据实际存放在哪里,但不影响恢复本身。
第 2 步:用 reflog 找到”丢失”的提交
git reflog -10reflog 记录了 HEAD 移动的历史,即使提交脱离了分支,只要它曾经是 HEAD,就会留在 reflog 里。这里找到两个关键提交:
6d2ded6 HEAD@{2}: commit: feat(clrjit): 添加 FunctionPointerDemo 示例项目及相关文件
aab3d12 HEAD@{3}: commit: feat(Harmony 2.x): 添加 Harmony 2.x 示例项目及相关配置文件
第 3 步:确认这些 commit 里的文件内容
# 查看某个提交里包含哪些文件
git ls-tree -r --name-only 6d2ded6 | grep -i functionpointer
git ls-tree -r --name-only aab3d12 | grep -i harmony
# 确认提交对象是否完整可读(未损坏)
git cat-file -p 6d2ded6^{tree} >/dev/null && echo "OK"第 4 步:从孤立提交单独检出目录(核心恢复动作)
# 只恢复这两个目录,不创建分支、不改 HEAD、不动其它文件
git checkout 6d2ded6 -- FunctionPointerDemo/
git checkout aab3d12 -- HarmonyDemo/说明:
git checkout <commit> -- <path>会把指定路径从那个提交恢复到工作区 + 暂存区,是非破坏性操作,不会影响其它未提交的改动。
第 5 步:确认并提交
# 查看恢复的文件(显示为 A = 新添加到暂存区)
git status --porcelain FunctionPointerDemo/ HarmonyDemo/
# 提交,让它们重新进入历史
git commit -m "chore: restore FunctionPointerDemo & HarmonyDemo from dangling commits"关键概念
什么是 Dangling Commit(孤立提交)?
当提交被 checkout、reset、merge 等操作”跳过去”后,如果没有分支或 tag 引用它,它就变成 dangling。但它不会立即被清除,对象仍保存在 .git/objects/ 中,直到被 git gc 真正清理。因此:
- ✅ 只要 reflog 还在,就能找回
- ⚠️ 长时间后或执行
git gc --prune后,孤立对象可能被永久清除
为什么源码会”丢失”?
本例根因是 fast-forward 合并:pull 时远程历史是本地历史的”超集”,git 直接快进。但这两个项目所在的本地提交(6d2ded6、aab3d12)不在远程分支上,快进后这些提交脱离了 main,工作区对应文件被远程版本覆盖/清理。
常用排查命令速查
| 目的 | 命令 |
|---|---|
| 查看提交包含的文件 | git ls-tree -r --name-only <commit> |
| 查看提交内容 | git show <commit> |
| 找回丢失的提交 | git reflog |
| 某提交在哪些分支 | git branch --contains <commit> |
| 某提交在哪些远程分支 | git branch -r --contains <commit> |
| 恢复某目录到工作区 | git checkout <commit> -- <path> |
| 检查对象是否损坏 | git cat-file -p <commit>^{tree} |
| 查看所有孤立对象 | git fsck --lost-found |
预防建议
- pull 前先 commit:确保本地改动都提交了,避免被快进覆盖。
- 用
git pull --rebase代替默认 merge,减少历史分叉。 - 定期
git fsck --lost-found:及时发现并备份孤立对象。 - 关键源码尽早推送远程,避免只存在于本地分支。
场景仓库:Windows-API-Usage/.Net-Code,恢复 2026-08-15