3.3 撤销三兄弟:reset、checkout、revert


3.3 撤销三兄弟:reset、checkout、revert

本节摘要:撤销手段按"动不动引用、动不动 index、动不动工作区"分类就再也不会混:reset 挪分支指针有三档力度,restore/checkout 覆盖工作区或 index,revert 用新提交反向抵消旧提交。最后是一套 reflog 自救实录——对象模型侦探的看家本领。

学习目标

  1. 能说出 reset 三种模式各自影响哪些区
  2. 能判断当前场景该用 reset 还是 revert
  3. 能解释 revert 一个 merge commit 为何必须给 -m 父序号
  4. 能完整演示一次"reset --hard 之后找回代码"的自救

一、reset:挪指针的三档力度

git reset <目标> 的核心动作只有一个:把当前分支引用挪到目标提交。三档力度决定顺带回填多远:

git reset --soft HEAD~1 # 只挪指针 index 工作区都不动 git reset --mixed HEAD~1 # 挪指针 并把index回填成目标提交 默认档 git reset --hard HEAD~1 # 指针 index 工作区全部对齐目标 快刀斩乱麻

用三区模型演绎一遍"提交后想重来":soft 档下,刚才提交的内容仍完整躺在 index 里(状态显示为"已暂存"),改改说明就能重新 commit;mixed 档下内容退回"未暂存",重新分袋;hard 档连工作区文件都回到提交前,未提交的改动原地蒸发。三档的共同安全边界:被 reset 跳过的提交本身仍是对象库里的完整证物,reflog 记录着指针的移动轨迹,随时能回去。

reset 的定位是"重写本地未共享的历史"。一旦提交已被推送到共享分支,挪指针就意味着下次必须强推——2.3 的黄金法则再次生效。共享历史的撤销,用 revert。

二、revert:用新提交抵消旧提交

git revert <哈希> 做的是"反做":以该提交的补丁取反,生成一个新提交。历史只增不减,不需要强推,这就是它在公共分支上不可替代的原因。

git revert e5f6a7b # [main 9d0e1f2] Revert "dev edit"

新提交的 diff 恰好是旧提交的镜像,两者叠加等于什么都没发生,但卷宗里永远留着"做过又撤销"的记录——审计视角这是优点,洁癖视角这是噪音,取舍属于团队文化。

revert merge commit 的特殊参数:merge commit 有两个 parent,Git 必须知道"撤销后回到哪一边",-m 1 表示以第一父(主分支侧)为基准:

git revert -m 1 <merge提交哈希>

不给 -m 直接拒绝执行。更凶险的是后续:revert 掉一个 merge 之后,被合并的那批提交若将来要重新合入,不能直接 merge——那批提交的哈希在主线上"已被记录为撤销",Git 会认为它们已存在。正确做法是先 revert 那次 revert,或对内容重新提交。这个连环坑坑过无数团队,值得写进任何一本 Git 教材。

三、restore 与 checkout:覆盖式回退

细粒度回退用 restore(3.1 与 1.3 已铺垫):git restore --staged 文件 退袋、git restore 文件 用 index 覆盖工作区、git restore --source=HEAD~2 文件 用任意历史快照覆盖工作区。它们不碰提交链、不碰引用,是最安全的撤销层。

四类撤销的边界一表定乾坤:

命令 引用 index 工作区 新提交 适用
restore 文件 覆盖 丢弃工作区改动
restore --staged 回填 退袋
reset --soft 本地重来 分袋不变
reset --hard 回填 覆盖 本地全部不要
revert 公共历史撤销

四、自救实录:reflog 是监控录像

完整的实战剧本。背景:功能分支上干了半天,误以为代码已提交,一把 git reset --hard origin/main 想回到主干状态:

git reflog # a1b2c3d HEAD@{0}: reset: moving to origin/main # e5f6a7b HEAD@{1}: commit: wip 登录修复 <- 灾难发生前的顶点 # 3f4g5h6 HEAD@{2}: commit: wip 第一步 # ...

reflog 按 HEAD 移动逐条记录,@{n} 是倒数第 n 次的位置。找回:git reset --hard HEAD@{1},工作区瞬间恢复到 reset 前的提交。若丢的是"从未提交过"的工作区改动——对象库里没有它的 blob,reflog 也无能为力,这正是 1.3 强调的"危险边界:未提交内容不受保护"。若丢的是某次被 rebase 洗掉的旧提交,同样手法:reflog 里找到变基前哈希,git branch rescue <哈希> 给它发一本"户口"。

reflog 默认保留 90 天(不可达引用 30 天),配合对象库两周以上的滞留期,构成了 Git 的"证据有效期"。定期把重要工作 push 到远端,才是终极备份——这也正是下一章的主题。

⚠️ 常见坑:在公共分支上 reset --hard origin/main 而本地领先若干提交,push 时被拒,情急之下 force push 把同事的工作一并抹掉。链条断在两个环节:推送前不看 git log main..dev 自查,强推不用 --force-with-lease 护栏。两个习惯都该写进团队手册。

💡 关键直觉:判断撤销手段,先问两个问题——"这段历史有没有别人引用?"有则 revert,无则 reset;"要撤的是提交还是工作区改动?"提交用 reset/revert,工作区用 restore。两问过后,四条命令各就各位。

本节要点回顾

  • reset 三档:soft 保袋、mixed 退袋、hard 清场;共同前提是历史未共享。
  • revert 反做:公共分支唯一正规撤销法;merge 必须 -m,revert 后重合入有连环坑。
  • restore 最安全:只覆盖 index 或工作区,不碰历史。
  • reflog 自救:HEAD 移动的监控录像,@{n} 定位、reset 找回;未提交内容是盲区。
  • 两问定命令:是否已共享、撤提交还是撤改动。

第 3 章结案。你已经具备单机作战的全部侦探技能——第 4 章把对象库接入网络,进入团队协作的领域。


作者与出处
原作者: 灏天文库
来源:灏天文库
整理: 灏天文库整理
由灏天文库平台收录,内容或由平台用户上传,仅供学习交流
发布者: 作者: 灏天文库 转发
评论区 (0)
U