先记住:绝大多数操作都能恢复
Git 的提交几乎不会真正消失:reset、rebase、误删分支之后,提交对象仍然留在对象库里,直到被垃圾回收。reflog 记录了 HEAD 的每一次移动,是找回内容的第一选择——先用 git reflog 找到目标哈希,再 git checkout <hash> 查看,或 git reset --hard <hash> 回到那个点。
日常高频命令
| 场景 | 命令 | 说明 |
|---|---|---|
| 查看状态 | git status -sb | 简洁显示分支与改动 |
| 暂存部分改动 | git add -p | 逐块选择,避免误提交调试代码 |
| 临时存放 | git stash push -m "msg" | 切分支前暂存,回来用 pop 恢复 |
| 修改上一次提交 | git commit --amend | 仅用于尚未推送的提交 |
| 查看某行的来源 | git blame -L 10,20 file | 定位问题引入点 |
| 二分定位坏提交 | git bisect start | 在好/坏提交之间自动二分 |
四类常见事故与处理
- 提交错分支:git reset --soft HEAD~1 撤回提交但保留改动,再切到正确分支提交;
- 已推送的错误提交:不要对公共分支 force push,改用 git revert <commit> 生成反向提交;
- 误删分支:git reflog 找到该分支最后一次提交的哈希,git branch <name> <hash> 重建分支;
- 改动被覆盖:git fsck --lost-found 可找回悬空对象,配合 git show <hash> 确认内容后再恢复。
团队协作的三条底线
- 公共分支不 rebase、不 force push:会重写他人已经基于其工作的历史;
- 提交信息写清「为什么」:改动本身看 diff 就知道,动机看不出来;
- 大改动拆成小提交:便于回滚、评审与二分定位。
建议的分支与提交节奏
- 从主干拉出短生命周期分支,一个分支对应一个可交付的改动;
- 本地先整理提交(squash / fixup),再推送到远端;
- 合并前同步主干,冲突在本地解决,不在网页端硬改;
- 合并后删除分支,保持仓库整洁。
常见问题
reset、revert、checkout 怎么选?reset 移动分支指针(改写历史,适合未推送的提交),revert 生成反向提交(不改写历史,适合已推送的提交),checkout 只是切换去查看。git pull 和 git fetch 有什么区别?pull = fetch + 合并;想先看清差异时,用 fetch 再手动合并更安全。如何撤销已 add 但未提交的内容?git restore --staged <file>,工作区改动会保留。
分支策略与协作约定
日常使用 Git 时,真正影响协作效率的不是命令熟练度,而是分支与提交的约定是否清晰。
- 提交要能单独回滚:每个提交应完成一件完整的事,避免把格式化与功能修改混在一起。混合提交会让回滚变成两难:要么带着无关改动回退,要么放弃想保留的修改。
- 提交信息写清“为什么”:标题说明改了什么,正文说明为什么这样改、有哪些取舍。半年后回溯时,原因比改动本身更有价值。
- 小步提交、及时合并:长期存在的分支会持续累积冲突,且与主干差异越大,合并风险越高。应以天为单位合并,而不是以周或月。
- 保护主干:主干禁止直接推送,要求评审与自动化检查通过。强行推送与改写公共历史应被明确禁止,它会破坏其他人的本地仓库。
- 约定同步方式:明确团队使用变基还是合并来同步主干,并保持一致。两种方式混用会造成历史难以阅读,也容易出现重复提交。
历史清理的边界
改写历史只应作用于尚未共享的本地提交。已经推送并被他人拉取的分支不应重新改写,遇到错误应使用反向提交来纠正,这样所有人的本地仓库都能正常同步。
仓库体积与历史治理
长期运行的仓库要关注体积增长,尤其是曾经提交过大文件或二进制资源的情况。定期检查包体积与大对象清单,必要时用专门工具做历史瘦身。日常提交时避免把构建产物与依赖目录纳入版本控制,能省去日后大量清理工作。