在大屏或电脑上体验更佳——动画和操作区域更宽敞。
输入

撤销修改

Git 中的撤销可能是复制快照、移动引用,也可能是创建反向修改的新提交。git restore、git reset、git revert 最后能得到相同文件内容,却留下不同索引、分支历史、对象数和恢复指针。

工作区、索引、HEAD -> main 所指提交是三个不同位置。restore 复制文件版本;reset 移动分支,再按模式恢复文件层;revert 新增纠正记录。文件内容相同,不代表历史相同。

三种撤销如何工作

git restore app.txt 默认从索引覆盖工作区,不移动 main、不改索引、不创建对象。加 --staged 后,默认来源变成 HEAD 树,目标变成索引。restore 处理文件层,不改提交历史。

git reset <target> 先解析目标,将旧末端保存到 ORIG_HEAD,再移动 main,模式决定后续复制:

模式移动 main目标复制到索引目标复制到工作区新提交
--soft是否否否
--mixed是是否否
--hard是是是否

soft 可移动分支而保留原索引及工作文件;mixed 再替换索引;hard 还覆盖已跟踪工作文件。ORIG_HEAD 只保存提交 ID,不备份从未暂存或提交的编辑。

git revert HEAD 则比较选中提交与父,计算逆向修改,应用到索引和工作区,再创建新提交并推进 main。被撤销提交仍是祖先。revert 不向后移动分支,而是用新快照记录纠正。

撤销修改逐步演示

默认从未诞生的 main 开始:

  1. edit app.txt 只在工作区写 mode=safe。
  2. git add app.txt 保存 blob,让索引指向它。
  3. git commit -m "base" 写索引树和根提交 C1,再推进 main。三个文件层都为 mode=safe。
  4. 第二次编辑只把工作区改为 mode=draft。
  5. git restore app.txt 从默认来源索引复制 mode=safe,draft 消失,main 和索引不变。
  6. 再编辑工作区为 mode=unsafe。
  7. add 保存 unsafe blob 并更新索引。
  8. git commit -m "bad-change" 创建父为 C1 的 C2,再推进 main,三层都是 unsafe。
  9. git reset --soft HEAD^ 解析 C1,将 C2 写入 ORIG_HEAD,main 后退到 C1。索引和工作区仍为 unsafe,所以相对新 HEAD 已暂存修改。
  10. git reset --soft ORIG_HEAD 先解析保存的 C2,再用 C1 覆盖 ORIG_HEAD,并把 main 前进到 C2。两文件层不变。必须先读目标再覆盖恢复指针,否则会重置到错误提交。
  11. git revert HEAD 确认索引、工作区都匹配 C2,由 C2 与 C1 求出 mode=unsafe -> mode=safe 并应用。可复用 C1 的树,只新增提交 C3,不创建新 blob 或树。main 进到 C3,C2 作为其父仍可达。

最终文件与 C1 同为 safe。hard reset 到 C1 也能得到该内容,但分支向后且只存两个提交;revert 则让分支向前到第三个提交,父还是错误提交。单看文件无法判断操作历史。

每种命令写入哪里

命令工作区索引main对象库
restore app.txt从索引复制不变不变不变
restore --staged app.txt不变从 HEAD 复制不变不变
reset --soft不变不变移动不变
reset --mixed不变从目标复制移动不变
reset --hard从目标复制从目标复制移动不变
revert HEAD逆向结果逆向结果前进本轨迹新增一个提交

单文件引擎中,复制或哈希 B 字节为 O(B);单父邻接目标 HEAD^ 的解析为 O(1);分类 main 可达提交为 O(V + E)。这是浏览器模型的成本,不是 Git 索引、pack、版本遍历器或文件系统的性能保证。

边界情况

  • 未诞生 HEAD 没有树,restore --staged 无默认来源。
  • 根提交没有第一父,HEAD^ 解析失败,不等于空树。
  • ORIG_HEAD 在本模型首次成功 reset 前未设置。未知或有歧义的短 ID 也会失败且不修改状态。
  • 目标已与来源相同,restore 无变化,不额外写对象或引用。
  • 索引或工作区与 HEAD 不同时,本模型拒绝 revert,避免模拟未实现的冲突流程。
  • hard reset 能丢弃 Git 从未保存成对象的工作字节,提交恢复指针不能恢复这些编辑。

常见错误

  • 说 restore 移走文件。它复制快照到目标,来源仍在。
  • 说 reset 删除提交。它移动引用,旧提交可能仍由 ORIG_HEAD 等指向,对象清理另行处理。
  • 把 ORIG_HEAD 当 reflog。前者只保存一个提交 ID,后者是独立的引用更新历史。
  • 说 revert 存补丁对象。它计算逆修改,再把结果树和父记录为新提交。
  • 因为提交可恢复,就认为 hard reset 的全部结果都可恢复。被覆盖且未存储的工作编辑可能无法找回。

模型范围

本课只有本地仓库、附着 HEAD -> main、一个普通已跟踪文件、单父历史,展示整文件 restore、三种 reset 和干净的 revert HEAD。身份固定,使用真实 Git 格式 SHA-1 ID。

省略路径集合、补丁模式、删除、重命名、文件模式、未跟踪文件阻挡、分离 HEAD、多分支、合并提交、revert -m、旧提交冲突、revert 序列器、钩子、签名、远程、reflog 过期、垃圾回收及编辑器或文件系统恢复。实际操作的选项和覆盖行为见 git restore、git reset、git revert。