LWN:git evolve - 跟踪一个改动自己的历史!

关注了就能看到更多这么棒的文章哦~

Git evolve: tracking changes to changes

By Jonathan Corbet
November 11, 2022
DeepL assisted translation
https://lwn.net/Articles/914041/

Git 源代码管理系统创建出来,就为了跟踪其中所有文件的变动;Git 仓库中的 commit 记录就反映了这些文件的变动历史。不过,在 Git 中看到的是这些 commit 之后走到的最终样子;而每个 patch 本身在被接受合入的过程中所经历的变动并不会记录下来。而这段历史也是有价值的,尤其是在这个改动还在商讨过程中的阶段。人们就提出了 git evolve 子命令,用来展示这些改动本身的变化过程,从而让人们可以利用工具来深入其中。

有些 patch 在写完后很快就会合入到项目的 git 仓库中,但还有很多 patch 都是需要更多的工作的。例如 stackable security modules 的功能,在多年来已经经历了(至少)38 版修改。在这项工作后面真的合入 Linux 内核 mainline 时,其实跟许多年前最初发布的内容已经没有太多相似之处了。每次修订都会经历一些变动,在这 39 个 patch set 中这些变动会涵盖大部分内容。Git 可以支持查看这么多版本的变动,但用起来有点麻烦,导致许多开发人员使用其他工具(如 Quilt)来管理正在进行的工作。

Commits, meta-commits, and changes

人们提出的 Git evolve 功能,针对 "meta-commits" (可以认为是某个特定 commit 的多个历史版本)增加了一个新的记录层。这个 meta-commit 描述了某个特定 commit 的历史,它被被保存在一个特殊的 branch 中,这个分支被命名为 "change"。文档中把 "meta-commit" 和 "change" 这两个名词混用,看起来像是可以互换使用的。在大多数情况下,它们可以被认为是一样的。meta-commit 只是保存了一个 change 的历史,也就是记录了一个特定的 commit 随着时间的推移所经历的演变。

我们来讨论一个更加深入的例子:如果某个开发者做了一些工作并且生成了 commit,那么就会得到一个 commit(用哈希值标识,我们在这个例子中称之为 A)和一个 meta-commit,这个 meta-commit 会存储在一个新的 change branch 中,名称为 metas/mc1(这些 change 的命名是另一个单独的话题,有一个强制性的 hook,方便用户可以添加脚本来生成自己的命名)。其最终结果是一个 structure,按编者的绘图技能来的话,可以如下表示:

a4f19cfd720062caa23942d6520487a3.png

[第一次提交]

这里我们看到本地 git 仓库的 trunk 分支上的新提交 A,而 change 分支 metas/mc1 则包含了一个 meta-commit,会记录了该 commit 的哈希值。

假设这个 commit 和很多 commit 一样,最初的版本并不完美,需要改进。如果这个开发者后来使用 git commit –amend 这样的命令来修改这个 commit,Git 就会更新 metas/mc1,从而记录修改后的 commit 的哈希值(这里是 B),但务必注意,这个 commit "废除" 了 commit A。

4ec7bfb5afa8cf90abebf81164bc76b0.png

[第一个提交,已修改]

旧的提交 A 将会保留在 git 仓库中,如果以后有人想看看 A 和 B 之间有什么变动的话,可以直接对比。

如果开发者又在同一分支上添加另一个 commit C(没有使用–ammend 参数),那么就会得到另一个 change 分支,名为 metas/mc2,指向了这个新的 commit。

6d0ccc294c537f056a789bc7b8821dd1.png

[第二个提交]
因此,一组很多 patch 历史中,会有许多活跃的 change 分支,这一组 patch 中的每个 commit 都有一个。值得注意的是,上述机制可以确保这一系列中每个 commit 的 change 名称都保持稳定,不受 commit 本身变化的影响。这一系列 commit 中的第一个是 metas/mc1,即使该 commit 本身随着时间的推移和哈希值的变化而改变过了。可以使用一组命令来列出已知的变动,可以用简单的 git reset 或 git checkout 命令将 branch 给重置到特定的 change 上。

现在假设提交 B 需要进一步修改;我们的开发者不小心忘记了在他们的变量声明中按照 reverse Christmas-tree 来进行排序,并且被人批评了。那么他们可以用 git reset 回到那个 commit,也就是 metas/mc1 所指向的那个,来解决这个错误。稍微编辑一下之后再用 –amend 来提交一个新的 git commit,这就会产生一个新的 commit D,同时 metas/mc1 会被更新,从而将 commit B 淘汰。

06897903e03408147cc2402ba6d1acf2.png

[修改第一个提交]

这组 commit 中的第一个已经被更新过了,但现在第二个提交(C),也就是 metas/mc2 所指向的那个,仍然是以旧的 commit B 为父 commit ,所以这组 patch 已经分叉了。如果开发者现在运行 git evolve,所有基于 metas/mc1 的改动(无论是哪个版本)都会进行 rebase,从而重新创建出一个完整的 change history。

cc5c35dcbbadb832b1188f8a6eb16fca.png

[在 git evolve 之后]

原先名为 C 的 commit 已经在 D 的基础上 rebase 过了,从而恢复出了完整的这一系列 patch。也可以使用 git evolve 命令来更新一组改动到 git 仓库中的一个新的 base 位置,也就是 rebase 所有的 change,从而按照其他地方合并的改动来更新。

More than rebase

因此,git evolve 在使用上有点像 git rebase,但也有一些区别。也许最重要的一点是,commit 可以在这个 stream 中的各个地方进行修改,然后最后一起 evolve 一次。例如,一个开发者可以对一个由 12 个 patch 组成的一个系列中的第 3、7 和 9 个 patch 进行修改,每个都是独立进行的,然后用 git evolve 来在未来的某个时刻把这个系列重新排成一个 patch 系列。

还有另一个区别,change 的历史可能不是严格线性的。举个简单的例子,如果一个版本库中只有一个 commit,那么开发者可以修改该 commit 来创建一个新的 change,就像上图的 commit B。如果开发者使用了 git reset 回到修改前的 commit(也就是 A)并再次进行修改,那么现在会有两个修改版本,每个都会淘汰掉最初的 commit A。文档中称这种情况为 "divergence";一组 patch 系列的 change 历史可以包含随意数量的 divergence,以及建立在它们之上的 change。例如,一个 divergence 可能是由于尝试了另一个不同的 fix 方法而产生的。

Git 能够跟踪无数个 divergence,但最终总有一天这个问题会完成。比如说开发者运行了 git evolve,Git 就需要知道如何解决这个 divergence,从而能 rebase 这组 patch 中的其他那些。这时通常的解决方法是对 diverging change 进行合并(merge),但也可以直接选择其中一方的改动。

由于 change 本身就是一个 Git 分支,所以可以在 git 仓库之间 push 和 pull。因此,开发者可以与其他开发者或某种 change tracking 系统来分享他们的工作的当前状态,以及是如何达到当前这种状态的。任何一个能够访问 change 的人都可以查看到不同版本的 patch,从而看到这些工作在朝着什么方向走。

最后一点,change 是短暂的,因为它们只在它们描述的工作最终完成并提交到 trunk 分支时才有价值。走到最终这一刻的时候,change 应该已经达到完美状态了,那么这些关于它如何达到当前状态的历史也就不再有价值了。因此,每当 git evolve 命令看到一个 change 所指向的 commit 已经被合并了,那么它就会自动删除这些 change。所以一个开发者环境中当前存在的 change 通常反映了在这个时刻实际正在进行的工作。

An evolving story

上面的描述主要来自于一个描述了提议的功能的文档。这份文件很透彻、详细,但有点难读懂。不过编者本人只要读十几遍,就已经对这里的内容有了一个浅显的了解了。。。

git evolve 这个 patch 并不是新事物;实际上,它本身已经经历了相当多的演进。Stefan Xenos 在 2018 年底发布了这个功能的最初设计,2019 年 1 月推出了第一批实现 patch。截至目前,这些 patch 的最新版本是由 Christophe Poucet 在 10 月初发布的。这些年来,人们对这些 patch 一直很感兴趣,但该功能的复杂性也使得其他人难以 review。

因此,目前还不清楚 git evolve 是否会进入 Git 的 mainline。它有一些明确的使用场景,每个 change set 都引起了积极的讨论。不过,这项功能的好处最终能否超过其增加的复杂性,还是需要由 Git 维护者来评估。如果 evolve 的功能能够达到标准并合入,那么就可以增强 Git 的功能,而目前开发者必须利用其他工具才能实现类似的目的。因此,在 Git 中增加一些复杂的功能可能可以让人们的生活更加简单。

全文完
LWN 文章遵循 CC BY-SA 4.0 许可协议。

欢迎分享、转载及基于现有协议再创作~

长按下面二维码关注,关注 LWN 深度文章以及开源社区的各种新近言论~

format,png

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值