恢复能力取决于内容是否已保存为对象、引用与 reflog 是否保留,以及当前操作状态;.git 存在并不保证找回未提交编辑。
第一类:提交修正 (Commit Fixes)#
1. 刚刚提交完,发现漏了一个文件/写错了提交信息
- 症状:后悔药吃得晚了一秒。
- 处方:
git add <forgotten_file> git commit --amend --no-edit # 合并到上一次提交,不改信息 # 或者 git commit --amend -m "新的正确信息"
2. 把不该提交的大文件提交了,怎么彻底删掉?
- 症状:
.git文件夹瞬间爆炸,即使删除了文件,历史记录里依然占用空间。 - 处方:先建立独立备份,在新克隆中验证历史清理范围。Git 官方不推荐
filter-branch;更适合使用git filter-repo,例如以下会重写包含该路径的历史:
# 在专用于历史清理的新克隆中,路径为仓库相对路径占位符
git filter-repo --path <large_file_path> --invert-paths以下保留为旧教程命令的解释,参数不代表现代推荐流程,不应直接在共享仓库执行:
bash git filter-branch --force --index-filter \ 'git rm --cached --ignore-unmatch <large_file_path>' \ --prune-empty --tag-name-filter cat -- --all
清理后先检查分支、标签和对象范围,并与协作者协调重新克隆或重建引用。旧教程的 git push origin --force --all 会扩大覆盖范围,不能作为自动下一步;发布前限定每个 ref 并核对服务器保护。文件改名历史需要逐一纳入清理,单一路径过滤不一定覆盖旧名字。密钥泄露应先撤销或轮换,改写历史不能撤回别人的克隆。
第二类:分支救急 (Branch Rescue)#
3. 在错误的分支上写了半天代码,还没提交
- 症状:本该在
feature分支写,结果在master上写了一堆。 - 处方:
git stash -u # 1. 保存已追踪及未追踪内容(忽略文件另行处理) git switch <right_branch> # 2. 切换到正确分支 git stash pop # 3. 恢复现场
4. 代码写到一半,紧急需要修 Bug,但不想提交“半成品”
- 症状:代码处于不可编译状态,不想留下垃圾 commit。
- 处方:使用
Stash(见 Git 02)。git stash -u # 保存当前进度(含新文件) # ... 修 Bug ... git stash pop # 回来继续写
第三类:撤销与恢复 (Undo & Recovery)#
5. 执行了 git reset --hard,代码全丢了
- 症状:心跳停止,那是你一星期的心血。
- 处方:利用
reflog(见 Git 04)。git reflog # 找到 reset 之前的那个哈希值 (例如 abc1234) git branch recovery/saved-tip abc1234 # 先为确认过的提交建立恢复引用
6. 已经 Push 到远程的代码,想撤回
- 场景 A:分支历史改写已协调、允许替换远端末端。
- 先检查状态并备份;原教程中的
git reset --hard HEAD~1会丢弃本地修改,git push -f会覆盖远端历史,不能作为普通撤销步骤。若只需撤销末次提交同时保留文件,可以用git reset --soft HEAD~1;发布改写时使用限定目标及明确期望值的--force-with-lease(见 远程协作),仍须核对远端权限及协作者状态。
- 先检查状态并备份;原教程中的
- 场景 B:公共分支(禁止强制推送)。
- 处方:使用
revert。
git revert <commit_hash> # 产生一个“相反”的新提交,抵消之前的修改 git push - 处方:使用
第四类:同步与冲突 (Sync & Conflicts)#
7. Pull 失败,提示冲突,怎么优雅解决?
症状:满屏红色警告,代码里全是
<<<<<<< HEAD。处方:
- 手动编辑冲突文件,保留正确代码。
git add <conflicted_file>。git merge --continue或git commit。
** 放弃合并**:
git merge --abort- 效果:尝试重建合并前状态;若开始时已有未提交修改且又被编辑,可能无法完整恢复。
- 代价:你之前所有手动解决冲突的代码会全部丢失!它就像时光倒流,假装这次合并从未发生过。如果你已经花了一小时解冲突,千万不要手滑执行这个命令。
- 之后怎么办:检查
git status确认操作状态和保存的改动,再选择重新 merge、按协作策略 rebase 或保持独立分支。Pull 也可能使用 rebase;此时继续和放弃应使用git rebase --continue/git rebase --abort,不能混用 merge 命令。git reset --hard origin/<branch>会丢弃工作区修改并移动当前分支,且不自动 fetch,不能用来绕过冲突。
8. Fork 的仓库如何同步原作者的更新?
- 症状:你的仓库落后原作者几个月了。
- 处方:
git remote add upstream https://example.com/upstream/project.git git fetch upstream git switch master git merge upstream/master # 或者 rebase
终极生存法则#
- 及时保存:WIP 提交或适当 stash 可保存内容,但 reflog 会过期,不等于永久备份。
- 理解 HEAD:知道你现在在哪(哪个分支,哪个提交)。
- 先检查再恢复:找到候选提交后先建立恢复引用并检查,不以 hard reset 反复试探。
恢复的三个边界
reset --hard 会覆盖已追踪内容,也可能覆盖或删除阻挡目标路径的未追踪文件;并非通用的清空所有未追踪文件命令。已提交对象可在仍保留时通过引用或 reflog 找回,未提交编辑则未必存在对象。reflog 默认可达记录约 90 天、不可达记录约 30 天过期,实际配置、维护和对象回收决定窗口。
共享分支一般以 git revert 新增反向提交保留历史;revert 合并提交还需指定正确的主线父提交,并理解对后续合并的影响。所有继续、终止与清理命令都应先对应 status 所显示的操作。
参考与延伸
根据技术笔记重新整理,示例使用通用名称。