研究 Git:撤销提交与移动分支不是同一操作

线上发现某次改动有问题,撤销业务变化和删除历史记录是两种不同意图。

edyang · 2015-02-25

线上发现某次改动有问题,撤销业务变化和删除历史记录是两种不同意图。

本文使用Git 2.x 与独立演示仓库。Git(分布式版本管理)保存提交对象和引用,工作区、暂存区与提交历史不是同一份状态。每次操作先判断它改变哪个区域,再决定是否适合当前协作情境。

1. 从现象推到机制

revert 创建一个反向变化的新提交,保留发生过的历史;reset 移动引用,并按模式影响索引或工作区。共享分支需要让其他人理解变化来龙去脉,通常优先通过新增提交修复或撤销。

研究 Git:撤销提交与移动分支不是同一操作的处理、反馈与观察关系

图中的四个节点分别表示问题提交、撤销意图、反向提交、发布验证。顺着实线检查输入如何变成输出,再沿虚线核对反馈条件。图示表达的是本文讨论的关系,不是实测曲线或现场截图。

2. 最小实现与输入条件

在演示仓库先提交一个小改动,再用 revert 生成反向提交。检查前后文件差异和完整日志,不在真实工作目录练习破坏性模式。

1
2
3
4
git show --stat <demo_commit>
git revert <demo_commit>
git log --oneline -4
git diff HEAD~2 HEAD

示例在没有私人资料的临时仓库执行。先看 status、diff 与 log,再运行改变状态的命令;共享历史优先新增提交表达修复,不用改写历史掩盖已经发生的变化。

3. 用对照区分原因

下表列出需要主动改变的条件。预期行为来自机制分析,复现时还需记录运行环境和原始输出,不能将预期当作已经测得的结果。

改变的条件 需要验证的行为
撤销普通单父提交 形成新的反向提交
后续代码依赖被撤销内容 反向补丁可能需要人工调整
只想取消暂存 不应误选会丢工作内容的操作

对照 1:撤销普通单父提交

形成新的反向提交。观察时记录旧提交仍留在历史中。把这个条件与基准情形分开,有助于判断变化来自哪一层;若得到相反结果,应先核对前提,再修改实现。

对照 2:后续代码依赖被撤销内容

反向补丁可能需要人工调整。观察时记录新的依赖关系与冲突。把这个条件与基准情形分开,有助于判断变化来自哪一层;若得到相反结果,应先核对前提,再修改实现。

对照 3:只想取消暂存

不应误选会丢工作内容的操作。观察时记录目标是索引还是历史引用。把这个条件与基准情形分开,有助于判断变化来自哪一层;若得到相反结果,应先核对前提,再修改实现。

4. 适用条件与取舍

撤销程序不等于撤销数据迁移和外部动作。发布回退前要确认旧程序能否读取新数据,不能只看 Git 文件恢复了。

一次正常运行只覆盖了这组输入。继续扩展前,应把上面的异常对照保留下来,尤其关注“只想取消暂存”这一分支。后续方案若改变了图中任意一处依赖,原来的判断就要重新验证,不能仅凭界面相似或返回值相同沿用结论。

同月延伸

参考资料

  1. Pro Git 第二版
  2. Git 2.2.0 版本说明