JEP 296: Consolidate the JDK Forest into a Single Repository | 将 JDK 森林合并为单个存储库
摘要
将 JDK 存储库的众多分支合并为一个单一的存储库,以简化和优化开发流程。
非目标
将 FX 源代码添加到 JDK 存储库不在提案范围之内。
动机
多年来,JDK 的完整代码库已被分成众多的 Mercurial 存储库。在 JDK 9 中,有八个存储库:root、corba、hotspot、jaxp、jaxws、jdk、langtools 和 nashorn。
虽然多个存储库的模型带来了一些优势,但也有许多缺点,并且在支持各种理想的源代码管理操作方面表现不佳。特别是,无法对相互依赖的变更集的存储库执行原子提交。例如,如果今天单个错误修复或 RFE 的代码跨越了 jdk 和 hotspot 存储库,那么这两个存储库的更改无法在托管这两个不同存储库的代码库中原子地完成。跨多个存储库的更改是常见的情况;在 JDK 存储库中,已经有超过 1100 个 bug id 在存储库之间重复使用。超过 1100 个跨存储库的 bug 仅是逻辑上跨存储库 bug 数量的下限,因为一些工程师使用单独的 bug id 来推送到不同的存储库。
这种 Mercurial 存储库的划分与工程的统一不匹配,削弱了现代源代码管理的主要优势之一:跟踪文件集的变更而不仅仅是单个文件。作为一个推论,SCM 事务和逻辑事务之间的不匹配复杂化了使用像 Mercurial bisect 这样的工具。
各个存储库与整个 JDK 没有独立的开发周期;所有存储库都与 JDK 推进周期同步前进。存储库的多样性对于新开发人员来说是一个比必要的门槛,并且已经导致了诸如“获取源代码”脚本等变通方法的出现。
描述
为了解决这些问题,我们开发了一个合并的原型代码库。原型可在以下位置找到:
http://hg.openjdk.java.net/jdk10/consol-proto/
用于创建原型的一些支持转换脚本作为unify.zip
附件附带。
在原型中,使用自动化转换脚本将这八个存储库合并为一个单一的存储库,以文件级别保留历史记录,并且使用标记来标记 JDK 推进阶段的时间戳来同步合并的代码库。更改集的注释和创建日期也被保留。
原型还进行了代码重新组织。在合并的代码库中,Java 模块的代码通常组合在一个顶级 src 目录下。例如,在当前的 JDK 存储库中,有基于模块的目录,如
$ROOT/jdk/src/java.base
...
$ROOT/langtools/src/java.compiler
...
在合并的代码库中,这些代码被组织为
$ROOT/src/java.base
$ROOT/src/java.compiler
...
因此,在存储库的根目录下,模块中源文件的相对路径在合并和 src 目录组合之后仍然保留。
类似但不那么激进的重新组织也适用于测试目录,以从
$ROOT/jdk/test/Foo.java
$ROOT/langtools/test/Bar.java
变成
$ROOT/test/jdk/Foo.java
$ROOT/test/langtools/Bar.java
由于该工作目前只是一个原型,其中并不完全完成的部分可能会改进一些问题。HotSpot C/C++ 源代码已经移至与模块化的 Java 代码并列的共享 src 目录中。
尽管回归测试可以在当前原型的状态下运行,但将来可能会对 jtreg 配置文件进行进一步整合。
替代方案
一个替代方案是继续使用当前的存储库集合。在转移到单一存储库时,某些或所有存储库的历史记录可能已被丢弃,但这一点被拒绝了。考虑到简化起见,选择了合并核心子集而不是单一存储库。
测试
为了验证文件内容,在每个推进标记中,使用一个脚本来验证该标记处的拆分代码库的内容是否与该标记处的合并存储库的内容相匹配。对于最近的 JDK 9 标记,比较了相同标记下拆分代码库和合并代码库的构建结果;只有一些细微且可以解释的差异。
风险和假设
上述测试应该可以缓解文件损坏和错误构建的最严重风险。虽然原型中的大部分工作都已完成,但在将合并代码库投入生产之前,可能无法完成各种较小的支持性功能。合并前后的代码库在 Mercurial 意义上没有关联。必须使用差异(适当调整路径)进行前向和后向移植,而不是导出和导入更改集。