[您也可以查看此文档的 单页版本。]

发布 Subversion

本文档通常可以分为三个部分,按具体程度递增

Subversion 版本发布经理使用一组在名为 release.py 的 Python 脚本中编写的步骤。此脚本可用于执行发布过程中的大多数自动化步骤。使用 -h 选项运行它以获取更多信息。

除了以下项目特定指南外,有志于成为发布经理的人可能还想阅读有关一般 Apache 发布策略 的信息。它们有时看起来有点拼凑,但可以很好地了解一般最佳实践以及 Subversion 如何融入更大的 ASF 生态系统。

Subversion 发布流程

版本号

Subversion 使用“MAJOR.MINOR.PATCH” 版本号。我们不使用“偶数==稳定,奇数==不稳定”的约定;任何未限定的三元组都表示稳定版本

   1.0.1  -->  first stable patch release of 1.0
   1.1.0  -->  next stable minor release of 1.x after 1.0.x
   1.1.1  -->  first stable patch release of 1.1.x
   1.1.2  -->  second stable patch release of 1.1.x
   1.2.0  -->  next stable minor release after that

版本的顺序是半非线性的——1.0.3 可能 会在 1.1.0 之后发布。但它只是“半”非线性,因为最终我们会宣布一个补丁系列失效并告诉人们升级到下一个次要版本,因此从长远来看,编号基本上是线性的。

数字可能有多位数。例如,1.7.19 是 1.7.x 系列中的第十九个补丁版本;它是在 1.7.2 发布三年后,在 1.7.20 发布三个月前发布的。

Subversion 版本可以通过版本号后面的文本进行限定,这些文本都代表预发布过程中的各种步骤。从最不稳定到最稳定排序

限定符 含义 示例 svn --version 的输出
Alpha 我们知道或预计存在问题,但会征求感兴趣个人的测试。 subversion-1.7.0-alpha2 版本 1.7.0 (Alpha 2)
Beta 我们预计不会出现问题,但以防万一,请谨慎。 subversion-1.7.0-beta1 版本 1.7.0 (Beta 1)
RC (发布候选) 此版本是最终候选版本,如果未发现严重错误,将删除 -rc 标签,并将此版本的内容声明为稳定。 subversion-1.7.0-rc4 版本 1.7.0(候选版本 4)

当您使用 make install 安装 subversion-1.7.0-rc1 时,它仍然会像安装 "1.7.0" 一样安装。限定词是版本的元数据;我们希望每个后续的预发布版本都覆盖之前的版本,最终版本覆盖最后一个预发布版本。

对于工作副本构建,没有需要担心的 tarball 名称,但 svn --version 仍然会产生特殊的输出。

   version 1.7.1-dev (under development)

版本号是项目正在努力的下一个版本。重要的是要说明 "正在开发中"。这表明构建来自工作副本,这在错误报告中很有用。

Alpha 和 Beta 版本

当我们希望新功能在进入正式的 稳定期 之前得到广泛测试时,我们有时会发布 alpha 和 beta tarball。即使有 "alpha1"、"alpha2" 等版本,也不需要进行任何 beta 版本发布;我们可以直接跳到 "rc1"。但是,在某些情况下,beta 版本可能有用:例如,如果我们不确定 UI 决策,并且希望在将其固化为正式候选版本之前获得更广泛的用户反馈。

Alpha 和 Beta 版本仅供希望帮助测试的人员使用,他们理解在最终版本发布之前可能会出现不兼容的更改。签名要求由发布经理自行决定。通常,RM 仅要求每个平台 1 或 2 个签名,并告诉签名者,即使他们的测试发现了一些小错误,他们仍然可以签名,只要他们认为代码足够稳定,值得其他人进行测试。RM 应该要求签名者在签名时附上任何错误的描述,以便在宣布 alpha 或 beta 版本时发布这些问题。

当公开宣布 alpha 或 beta 版本时,应严厉警告分发包打包者不要打包它。请参阅 Hyrum K. Wright 的这封邮件,了解一个很好的模型。

破坏兼容性

我们的兼容性规则(详见 下方)仅在最终的 x.y.0 版本发布并获得认可后才开始生效。任何出现在主干或 alpha/beta/rc 预发布版本中的 API、线协议和磁盘格式都不受任何兼容性承诺的约束;如果我们发现有充分的理由,我们可能会在最终发布之前随意更改它们。我们甚至可能完全删除我们不喜欢的接口或序列化格式。

这对于持久性数据(工作副本和存储库)来说尤其是一个问题,因为我们可能不会在最终发布时提供升级路径——用于旧格式的代码路径或指定脚本。(当然,我们可能会为测试我们预发布版本的开发人员和用户提供此类脚本;但我们没有义务这样做。)因此,永远不要将预发布版本用于任何需要长期安全保存的数据。

同时,我们想提醒读者,指出 API 设计错误的最佳时机是在它们发布并固定之前——换句话说,在初始设计和预发布阶段。

发布名称的重复使用

如果由于某些非代码问题(例如,打包故障)需要快速重新发布某个发布版本或候选发布版本,则可以使用相同的名称,只要该压缩包尚未 通过签名获得认可。但是,如果它已上传到标准分发区域并带有签名,或者重新发布是由于用户可能运行的代码更改造成的,那么必须丢弃旧名称并使用下一个名称。

如果在发布和宣布给用户之前丢弃了旧名称,则该丢弃的名称被视为非公开发布,并且文档(例如 CHANGES 和标签的日志消息)应更新以反映这一点。(参见 1.8.7 作为示例。)丢弃的发布版本的标签和压缩包仍然保留在存储库历史记录中,但它们不受支持用于一般用途(相反,它们已知存在发布阻碍错误)。

版本间兼容性

Subversion 遵循严格的兼容性原则,与 APR 的指南一致(参见 https://apr.apache.org/versioning.html),并在此基础上进行了一些扩展,将在后面描述。这些指南旨在确保各种形式的客户端/服务器互操作性,并确保用户在 MAJOR.MINOR 版本的 Subversion 之间拥有清晰的升级路径。

兼容性可以跨越多个方面:从 API 和 ABI 到命令行输出格式。我们试图在修改现有架构以支持新功能的同时,尽可能地支持当前用户。总体思路是

  1. 在同一 MAJOR.MINOR 版本线中,不同补丁版本之间的升级/降级永远不会破坏代码。它可能会导致 bug 修复消失/重新出现,但 API 签名和语义保持不变。(当然,语义可能会以适合 bug 修复的微不足道的方式发生变化,但不会以强制调整调用代码的方式发生变化。)

  2. 升级到同一主要版本线中的新次要版本可能会导致出现新的 API,但不会删除任何 API。任何针对旧次要版本编写的代码都可以在该版本线中的任何更高次要版本上运行。但是,如果编写了利用新 API 的新代码,则之后降级可能无法正常工作。

    (偶尔,会发现需要稍微修改旧 API 行为的 bug。这通常只会在各种极端情况和其他不常见区域表现出来。这些更改记录在每个 MAJOR.MINOR 版本的 API 勘误表 中。)

  3. 当主要版本号发生变化时,所有保证都失效。这是唯一一次可以完全重置 API 的机会,虽然我们尽量避免无故删除接口,但我们会利用它来进行一些清理工作。

Subversion 扩展了 APR 指南,以涵盖客户端/服务器兼容性问题

  1. 服务器(或客户端)的补丁或次要版本号版本永远不会破坏与同一主要版本线中的客户端(或服务器)的兼容性。但是,该版本提供的新的功能可能在没有相应升级到连接的另一端的情况下不受支持。对于更新 ra_svn 代码,请特别注意以下原则

    1. 可以向任何元组添加字段;旧客户端将简单地忽略它们。(目前,编组实现不允许您在元组的可选部分放置数字或布尔值,但更改这一点不会影响协议。)

      当向 API 调用添加信息时,我们可以使用此机制。

    2. 在建立连接时,客户端和服务器交换一个功能关键字列表。

      我们可以使用此机制进行更复杂的更改,例如引入管道或从 API 调用中删除信息。

    3. 可以添加新的命令;尝试使用不支持的命令会导致错误,可以检查并处理。

    4. 协议版本号可以升级,以允许优雅地拒绝旧的客户端或服务器,或者允许客户端或服务器检测何时必须以旧的方式执行操作。

      这种机制是最后的手段,在能力关键字难以管理时使用。

  2. 工作副本和仓库格式对于同一次要系列中的所有补丁版本都是向后和向前兼容的。它们对于同一主要系列中的所有次要版本都是向前兼容的;但是,次要版本允许创建不适用于先前次要版本的的工作副本或仓库,其中“创建”可能意味着“升级”以及“创建”。

安全版本

偶尔,在 Subversion 中会报告或发现安全问题,这些问题在发布时需要特殊处理。一般的发布流程相同,开发人员如何处理这些问题的详细信息在 其他地方 有所介绍。

自定义版本

Subversion 不发布二进制软件包,而是依赖于 第三方打包者 来完成此操作。幸运的是,许多个人和公司已经自愿参与到这项工作中,我们感谢他们的努力。

如果您是第三方打包者,您可能会遇到需要为用户修复错误或进行其他更改的情况,并且您希望比标准的 Subversion 发布周期更快地将更改提供给他们。或者,您可能在本地维护了一组对目标受众有益的补丁。如果可能,建议使用 补丁流程,并将您的更改接受并应用到主干,以便在正常的 Subversion 发布计划中发布。

但是,如果您认为您需要进行 Subversion 开发人员社区不会广泛接受的更改,或者需要提供对未发布功能的早期访问,则应遵循以下指南。这些指南旨在帮助防止用户社区混淆,并使您的发行版和官方 Subversion 发行版尽可能成功。

首先,请确保您遵守 ASF 商标政策。您需要将您的发行版与标准的 Subversion 发行版区分开来,以减少您的自定义发行版可能造成的任何潜在混淆。

其次,考虑在公共 Subversion 存储库中创建一个分支来跟踪您的更改,并可能允许将您的自定义更改合并到主线 Subversion 中。(如果您还没有,请申请提交权限。)

第三,如果您的自定义发行版可能会产生与主线 Subversion 无关的错误报告,请与自定义发行版的用户保持联系,以便您可以拦截和过滤这些报告。但当然,最好的选择是根本不处于这种情况——您的自定义发行版与主线 Subversion 的差异越大,它带来的混淆就越多。如果您必须进行自定义发行版,请尽量保持它们是临时的,并且尽可能不产生差异。

弃用

当引入新的改进的 API 版本时,旧版本出于兼容性考虑会保留,至少保留到下一个主要版本 (2.0.0) 发布之前。但是,我们会将旧版本标记为已弃用,并指向新的版本,以便人们知道尽可能地使用新的 API。弃用时,请提及引入弃用后的版本,并指向新的 API。如果可能,请用新 API 的差异替换旧 API 文档。例如

   /**
    * Similar to svn_repos_dump_fs3(), but with a @a feedback_stream instead of
    * handling feedback via the @a notify_func handler
    *
    * @since New in 1.1.
    * @deprecated Provided for backward compatibility with the 1.6 API.
    */
   SVN_DEPRECATED
   svn_error_t *
   svn_repos_dump_fs2(svn_repos_t *repos,
                      svn_stream_t *dumpstream,
                      svn_stream_t *feedback_stream,
                      svn_revnum_t start_rev,
                      svn_revnum_t end_rev,
                      svn_boolean_t incremental,
                      svn_boolean_t use_deltas,
                      svn_cancel_func_t cancel_func,
                      void *cancel_baton,
                      apr_pool_t *pool);

当主要版本号更改时,一系列中“最佳”的新 API 通常会替换所有以前的 API(假设它包含了它们的功能),并且它将采用原始 API 的名称。因此,在 1.1.x 中将 'svn_repos_dump_fs' 标记为已弃用并不意味着 2.0.0 没有 'svn_repos_dump_fs',它只是意味着该函数的签名将不同:它将具有 1.1.x 中 svn_repos_dump_fs2(或 svn_repos_dump_fs3,或其他)所持有的签名。带编号后缀的名称消失,只有一个(闪亮的新)svn_repos_dump_fs 再次出现。

这种替换策略的一个例外是,当旧函数的名称本身就完全不令人满意时。弃用是一个修复的机会:我们给新 API 一个全新的名称,将旧 API 标记为已弃用,并指向新 API;然后在主要版本更改时,我们删除旧 API,但不会将新 API 重命名为旧名称,因为它的新名称很好。

稳定和维护版本

概述

次要版本和主要版本在发布之前会经历一个稳定期,并在发布后保持维护(修复错误)模式。为了开始发布过程,我们 基于最新的主干创建一个 "A.B.x" 分支

新 A.B.0 版本的稳定期通常持续四周,这使我们能够进行保守的错误修复并发现严重问题。稳定期从版本为 A.B.0-rc1 的发布候选包开始。随着阻塞错误的修复,可能会发布进一步的发布候选包;例如,如果发现一组语言绑定已损坏,那么在修复它们后发布新的发布候选包是谨慎的做法,以便可以测试这些语言绑定。

创建 A.B.x 分支后,永远不会直接向其提交源代码更改;更改将在下一节中描述的过程投票后,从主干回移植到 A.B.x 分支。

在稳定期的最后一周开始时,如果自上次发布候选包以来有任何待处理的严重更改,则应发布新的发布候选包。稳定期的最后一周保留用于关键错误修复;次要错误的修复应推迟到 A.B.1 版本。关键错误是指非边缘情况的崩溃、数据损坏问题、重大安全漏洞或其他同等严重的问题。

在某些情况下,稳定期将延长

svn-soak-management.png

  • 如果必须进行可能导致不稳定的更改才能修复错误,则整个四周的稳定期将重新开始。可能导致不稳定的更改是指可能以不可预测的方式影响 Subversion 的许多部分,或涉及添加大量新代码的更改。任何不兼容的 API 更改(只有在新版本是 A.0.0 版本时才允许)都应被视为可能导致不稳定的更改。

  • 如果在稳定期的最后一周进行了关键错误修复,则最后一周将重新开始。最终的 A.B.0 版本始终与一周前发布的发布候选包相同(以下讨论的例外情况除外)。

A.B.0 版本发布后,当错误修复需要时,补丁版本(A.B.1、A.B.2 等)就会随之而来。补丁版本不需要四周的浸泡,因为只有保守的更改才会进入该行。

哪些更改有资格进行回移植

某些类型的提交可以进入 A.B.0 而不重新开始浸泡期,或者进入后续版本而不影响测试计划或发布日期

  • 无需投票

    • STATUS 文件的更改。

    • 文档修复。

    • 作为发布簿记的正常部分的更改,例如,滚动发布创建和维护发布分支 中列出的步骤。

    • 由发布经理或经发布经理批准对 dist.sh 的更改。

    • .po 文件中的消息翻译进行更改或添加新的 .po 文件。

  • 有投票

    • 仅影响 tools/packages/bindings/ 的任何更改。

    • 对打印输出的更改,例如错误和使用消息,只要格式字符串“%”代码及其参数未被触碰。

注意:对消息翻译更改的要求比对 C 代码中的文本消息的要求更宽松。允许在 .po 文件中更改格式说明符,因为它们的有效性可以通过机械方式检查(使用 GNU gettext 的 msgfmt 上的 -c 标志)。如果使用 GNU gettext,这将在构建时完成。

当然,核心代码更改需要投票,并重新开始浸泡或测试阶段,否则更改可能会被测试不足。

投票概述和选民

对 A.B.x 行的更改必须首先在 A.B.x/STATUS 文件中提出。每个提案都包含一个简短的标识块(例如,主干或相关行提交的修订号,或者可能是问题号)、对更改的简要描述、最多一行关于为什么它应该在 A.B.x 中的理由,可能还有一些注释/问题,最后是投票。注释和问题旨在简要总结以帮助读者了解情况。不要使用 STATUS 文件进行实际讨论;请使用 dev@ 代替。

这是一个示例,可能与条目一样复杂

   * r98765 (issue #56789)
     Make commit editor take a closure object for future mindreading.
     Justification:
       API stability, as prep for future enhancement.
     Branch: 1.8.x-r98765
     Notes:
       There was consensus on the desirability of this feature in
       the near future; see thread at http://... (Message-Id: blahblah).
       Merge with --accept=mc.
     Concerns:
       Vetoed by jerenkrantz due to privacy concerns with the
       implementation; see thread at http://... (Message-Id: blahblah)
     Votes:
       +1: ghudson, bliss
       +0: cmpilato
       -0: gstein
       -1: jerenkrantz

任何人都可以投票,但只有完整提交者和相关区域的部分提交者拥有约束性投票。当提交者投出非约束性投票(例如,部分提交者投票赞成其提名区域之外的更改)时,他们应该将他们的投票标记为非约束性,如下所示

 * r31833
   svndumpfilter: Don't match prefixes to partial path components.
   Fixes #desc4 of issue #1853.
   Votes:
     +1: danielsh, hwright
     +1 (non-binding): stylesen

这种区别在所有类型的投票中都有体现——回退投票、发布投票以及神话般的 由无法达成共识而导致的投票——但原因不同。在发布投票中,这种区别在法律上要求发布进入 ASF 的法律保护范围,而在回退投票中,它更接近于咨询性区别。毕竟,如果有人出于正当理由对更改投了 -1 票,他们的认证无关紧要;如果他们的分析是正确的,更改将不会被回退。同样,如果更改未能获得 所需的绑定 +1 票数量,但有一些非绑定 +1 票,这可能有助于它获得批准。

换句话说,回溯流程的目的是确保不稳定的更改不会进入补丁版本。投票通过强制对每个更改进行一定程度的审查来实现这一目的。由于 karma 不可转让,我们用绑定投票来衡量“审查程度”——但与以往一样,任何人都可以对流程进行输入并得到倾听。

投票类型

投票者对更改的意见被编码为 +1、-1、+0 或 -0。

定义“否决”或参考定义

如果您投了否决票(即 -1),请在“问题”字段中说明原因,并包含列表讨论的 URL/消息 ID(如果有)。如果您在提交否决票时没有可用的线程,您可以稍后返回并添加链接。

-0 票表示您对更改有些反对——在 dev@ 上说明您的理由,或在括号中进行总结——但不会阻碍共识。

对更改投 +1 票并不仅仅意味着您原则上同意它。这意味着您已经彻底审查了更改,并发现它正确且尽可能地不具破坏性。当它被提交到发布分支时,日志消息将包含所有投票赞成它的人员的姓名,以及原始作者和进行提交的人员。所有这些人被认为对错误负有同等责任。提交者被信任知道他们不知道什么,并且不会轻易投 +1 票。

如果您已经审查了补丁,并且喜欢它但有一些保留意见,您可以写“+1(概念)”,然后在列表中询问您关注的问题。如果您喜欢这个想法但没有仔细审查补丁,您可以写“+0”。这些投票都不计入总数,但它们对于追踪关注更改的人员并可能愿意花更多时间的人员很有用。

需要多少票

对于非 LTS(“常规”)发布线,如果一个变更获得了两个 +1 且没有否决票,则该变更被批准。(只有绑定投票有效;见上文。)

对于LTS 发布线,如果一个变更获得了三个 +1 且没有否决票,则该变更被批准。(只有绑定投票有效;见上文。)

尽管有上述规定,对于任何发布线,仅影响非核心代码(例如,tools/、packages/、bindings/、测试脚本等)且不影响构建系统的变更,可以获得一个 +1(来自该区域的完整提交者或部分提交者),至少一个 +0 或“概念 +1”(来自任何其他提交者),且没有否决票,即可被纳入。

目标是让至少两双眼睛审查变更,而不强求每个审阅者都拥有与区域维护者相同的专业知识。这样一来,可以审查变更的一般合理性、准确的注释、明显的错误等,而不必强迫审阅者“我理解这些变更的每一个细节,并且已经测试过它们”。

如何将变更提名到 STATUS

在将变更提名到 STATUS 之前,您应该尝试将其合并到分支中,以确保它不会产生合并冲突。如果发生冲突,请从发布分支创建一个新的临时分支,并将您的变更合并到该分支中并解决冲突。该分支的名称应为 A.B.x-rYYYY,其中 YYYY 是您在 STATUS 文件中变更的第一个修订版本。在 STATUS 文件中添加一条关于临时分支存在的注释,形式为 Branch: A.B.x-rYYYYBranch: ^/subversion/branches/A.B.x-rYYYY 标题(使用此确切形式;脚本会解析它)。如果变更涉及进一步的工作,您可以将这些修订版本合并到分支中。当此变更的条目从 STATUS 中删除时,此临时分支也应被删除,以避免使 /branches 目录混乱。

在极少数情况下,如果需要对 A.B.x 分支进行更改,而该更改不是从主干移植的更改,则也会使用临时分支。

如果您提名的条目会导致合并冲突,直到另一个提名被合并,请在提名中注明这一点。在条目中添加一个“Depends:” 标题;这将使每小时的“检测具有合并冲突的提名”构建机器人作业保持绿色。(“Depends:” 标题的值不会被解析。)

trunk 中的 tools/dist/nominate.pl 脚本可以自动添加新的提名。同一个脚本还包含一个 REPL 循环,可以帮助审查提名和投票;请参阅 trunk 中的 tools/dist/README.backport。

注意:关于临时分支的 STATUS 的更改,包括投票,始终保留在主发布分支上。

编辑 STATUS 和注释投票

当向其他人已经投票的提名添加修订时,请使用 "(rX only)" 对他们的条目进行注释,以说明他们投票了哪些部分,哪些部分没有投票,例如

   * r30643, r30653, r30785
     Update bash completion script.
     Votes:
       +1: arfrever (r30785 only), stylesen

明显的修复 不需要提及 "(rX only)"。

如果你提交了其他人通过 IRC 或其他方式传达的投票,请在你的日志消息中注明。

创建 Subversion 版本

通知翻译人员

在实际发布之前,请向翻译人员发送电子邮件,要求他们更新 .po 文件。从 trunk 中的 COMMITTERS 文件中获取他们的电子邮件地址,使用以下命令:

sed -e '/^ *Translat.*:$/,/:$/!d' -e 's/^ *[^ ]* *\([^>]*>\).*/\1/;t;d' COMMITTERS

包含由以下命令生成的翻译状态报告:

./tools/po/l10n-report.py

这应该在所有本地化字符串在发布中最终确定后完成,但要足够提前,以便翻译人员能够完成他们的工作。作为经验法则,在预期发布日期至少提前一周,最好更长时间发送通知。如果许多字符串发生了更改,例如从新分支发布的第一个版本,那么请留出更多时间。

准备发布版本

安装一些构建工具的原始版本

因此,发布分支已经稳定,您正在准备发布版本。发布过程的详细信息由 release.py 辅助脚本自动完成。要运行此脚本,您需要一个 Subversion trunk 工作副本。运行 release.py -h 以获取可用子命令的列表。

在您实际发布存档之前,您需要设置一个白盒发布环境。此环境必须包含一些构建工具的原始版本。

重要的是,您不要使用分发版附带的软件版本,因为它们通常会以不可移植的方式进行修补(例如,Debian 的 libtool 修补程序:#291641#320698)。tools/dist/release-lines.yaml 中给出的版本号通常应该重新考虑并增加到最新的稳定上游版本,在 A.B.0 版本发布之前的这段时间内。在 A.B.x 系列中更改版本应该谨慎考虑。

Autoconf、Libtool 和 SWIG:选择一个目录来存放您为 Subversion RM 任务准备的特殊构建工具,例如 /opt/svnrm。使用 release.py build-env 命令配置、构建和安装这三部分软件。

mkdir -p /opt/svnrm && cd /opt/svnrm && $SVN_SRC_DIR/tools/dist/release.py build-env X.Y.Z

安装更多构建工具

滚动脚本还需要以下命令可用:paxxgettextm4python -c 'import yaml'

从您的操作系统包中安装这些命令。(在 Debian 系统上,应该是 apt install pax gettext m4 python-yaml subversion。)

滚动发布

滚动发布前:

  1. 确保从 trunk 中获取的最新版本的 CHANGES 文件已合并到发布分支,并且 CHANGES 文件顶部的日期与计划的 tarball 发布日期匹配。(参见 管理 CHANGES 文件。)
  2. 确保 get-deps.sh 在发布分支上正常工作。

创建 tarball:运行

./release.py roll X.Y.Z 1234
当 release.py 完成后,您将在 /opt/svnrm/deploy 目录中找到 tarball。

测试一个或两个 tarball

    a) tar zxvf subversion-X.Y.Z.tar.gz;  cd subversion-X.Y.Z
    b) ./configure
 
       See INSTALL, section III.B for detailed instructions on
       configuring/building Subversion.
 
       If you installed Apache in some place other than the default, as
       mentioned above, you will need to use the same
       --prefix=/usr/local/apache2 option as used to configure Apache.
 
       You may also want to use --enable-mod-activation, which will
       automatically enable the required Subversion modules in the
       Apache config file.
 
    c) make
    d) make check
    e) make install (this activates mod_dav)
    f) make davcheck
 
       For this, start up Apache after having configured according to
       the directions in subversion/tests/cmdline/README.
 
       Make sure, that if you maintain a development installation of
       apache, that you check the config file and update it for the
       new release area where you're testing the tar-ball.
 
       (Unless you rename the tree which gets extracted from the
       tarball to match what's in httpd.conf, you will need to edit
       httpd.conf)
 
    g) make svncheck
 
       First, start up svnserve with these args:
 
          $ subversion/svnserve/svnserve -d -r \
            `pwd`/subversion/tests/cmdline
 
       -d tells svnserve to run as a daemon
       -r tells svnserve to use the following directory as the
          logical file system root directory.
 
       After svnserve is running as a daemon 'make svncheck' should run
 
    h) Then test that you can check out the Subversion repository
       with this environment:
 
          subversion/svn/svn co https://svn.apache.org/repos/asf/subversion/trunk
 
    i) Verify that the perl, python, and ruby swig bindings at least compile.
       If you can't do this, then have another developer verify.
 
       (see bindings/swig/INSTALL for details)
 
       Ensure that ./configure detected a suitable version of swig,
       perl, python, and ruby.  Then:
 
          make swig-py
          make check-swig-py
          sudo make install-swig-py
 
          make swig-pl
          make check-swig-pl
          sudo make install-swig-pl
 
          make swig-rb
          make check-swig-rb
          sudo make install-swig-rb

    j) Verify that the javahl bindings at least compile.
       If you can't do this, then have another developer verify.
 
       (see subversion/bindings/javahl/README for details)
 
       Ensure that ./configure detected a suitable jdk, and then
       possibly re-run with '--enable-javahl', '--with-jdk=' and
       '--with-junit=':
 
          make javahl
          sudo make install-javahl
          make check-javahl

    k) Verify that the ctypes python bindings at least compile.
       If you can't do this then have another developer verify.

       (see subversion/bindings/ctypes-python/README for details)

       Ensure that ./configure detected a suitable ctypesgen, and
       then possibly re-run with '--with-ctypesgen':

          make ctypes-python
          sudo make install-ctypes-python
          make check-ctypes-python 

    l) Verify that get-deps.sh works and does not emit any errors.

          ./get-deps.sh

使用 GnuPG 签署发布

使用 release.py 签署发布
    release.py sign-candidates X.Y.Z
这将为您运行以下命令的等效操作
    gpg -ba subversion-X.Y.Z.tar.bz2
    gpg -ba subversion-X.Y.Z.tar.gz
    gpg -ba subversion-X.Y.Z.zip
并将 gpg 签名附加到相应的 .asc 文件。

Subversion 操作

使用反映最终版本的 svn_version.h 创建标签。您可以使用以下命令:

    release.py create-tag X.Y.Z 1234

注意:请始终创建标签,即使是发布候选版本。

create-tag 将在原始分支的 STATUSsvn_version.h 文件中增加版本号。如果您刚刚完成了 1.0.2,那么这两个文件都应该具有 1.0.3 的正确值,依此类推。

将 tarball、签名和校验和提交到 https://dist.apache.org/repos/dist/dev/subversion。有一个 release.py 子命令可以自动执行此步骤。

release.py post-candidates 1.7.0

宣布候选版本可供测试和签署

  • 向 dev@ 列表发送一封电子邮件,类似于 此邮件

    Subject: Subversion X.Y.Z up for testing/signing
    
    The X.Y.Z release artifacts are now available for testing/signing.
    Please get the tarballs from
      https://dist.apache.org/repos/dist/dev/subversion
    and add your signatures there.
    
    Thanks!
    
  • 在 #svn-dev 上调整主题,提及“X.Y.Z 正在进行测试/签名”。

更新问题跟踪器以包含适当的版本/里程碑。如果发布新的次要版本,则应添加 1.MINOR.x 的版本。所有版本都应添加下一个版本的版本(即 1.MINOR.x+1)。如果您没有权限这样做,请在 IRC 上询问或向 dev@ 列表发送电子邮件。

签名源代码分发包(又称 tarball)

为什么发布签名

Subversion 版本通过全球 内容分发网络 (CDN) 分发。(这取代了 2021 年底之前的 ASF 镜像网络。尽管如此,可能存在其他组织选择继续镜像 ASF 版本。)

重要的是,最终用户能够验证他们下载的源代码包的真实性。校验和足以检测下载过程中的损坏,但为了防止恶意个人或镜像操作员分发替换包,每个源代码包都必须由 Subversion PMC 成员 进行加密签名。这些签名使用每个提交者的私有 PGP 密钥完成,然后与版本一起发布,以便最终用户可以验证下载包的完整性。

投票和签名要求

在 Subversion 版本正式公开发布之前,它需要

  • 来自 Subversion PMC 成员的三个 +1 票 [ASF 政策]
  • ,以及
  • 至少一名 PMC 成员在每个我们支持的主要平台上进行测试和签名:Windows 和 *nix [项目政策]

如何签名发布

在创建初始的 tarball 集时,发布经理也会创建第一组签名。虽然 tarball 本身可能是在 people.apache.org 上构建的,但重要的是签名不要在那里生成。签名 tarball 需要私钥,并且强烈建议不要在 ASF 硬件上存储私钥。在签名 tarball(使用以下过程)后,发布经理应将签名上传到初步分发位置,并将它们放置在与 tarball 相同的目录中。

鼓励 PMC 成员以及热心的社区成员从预发布位置下载 tarball,运行测试,然后提供他们的签名。这些签名的公钥应该通过 id.apache.org 包含在 ASF LDAP 实例中。(每天都会从 LDAP 自动生成 Subversion 提交者和 PMC 成员的当前公钥列表。)鼓励发布经理至少等待 5 天才能收到签名,然后再宣布发布,以便任何测试该版本的人员都可以在宣布之前完成对该版本的签名。

签署 tarball 意味着您断言它的一些内容。在宣布您的签名时,在邮件中说明您采取了哪些步骤来验证 tarball 是否正确,例如验证内容是否与存储库中的正确标签一致。在所有 RA 层和 FS 后端上运行 make check 也是一个好主意,以及构建和测试绑定。

要获取发布候选版本,请检出 https://dist.apache.org/repos/dist/dev/subversion 的工作副本。验证发布经理对 tarball 的 PGP 签名。release.py 自动执行此步骤

    release.py get-keys
    release.py --target /path/to/dist/dev/subversion/wc check-sigs 1.7.0-rc4

验证、提取和测试 tarball 后,您应该使用 gpg 创建一个带签名的分离签名。要将您的签名附加到 .asc 文件,请使用类似以下的命令

    gpg -ba -o - subversion-1.7.0-rc4.tar.bz2 >> subversion-1.7.0-rc4.tar.bz2.asc
release.py 脚本可以自动执行此步骤
 release.py --target /path/to/dist/dev/subversion/wc sign-candidates 1.7.0-rc4

添加签名后,将本地修改的 .asc 文件提交到 dist.apache.org 存储库。

如果您已下载并测试了 .tar.bz2 文件,则可以对具有相同内容的 .tar.gz 文件进行签名,而无需单独下载和测试它。诀窍是提取 .bz2 文件,并使用 gzip 压缩它,如下所示

    bzip2 -cd subversion-1.7.0-rc4.tar.bz2 \
    | gzip -9n > subversion-1.7.0-rc4.tar.gz

生成的文件应该与发布经理生成的文件相同,因此可以按照上述方法进行签名。要验证文件是否相同,您可以使用校验和或发布经理的签名,两者都应该与 tarball 一起提供。

但是,如果校验和不相同,并不一定意味着文件签名错误或文件被篡改。很可能是您没有与发布经理相同的 gzip 版本。

实际发布

创建 tarball 后,所有签名都已创建并验证,发布准备发布。本节概述发布 Subversion 版本所需的步骤。

上传发布版本

Subversion 工件通过全球 内容分发网络 (CDN) 分发。源代码 下载页面 会自动帮助用户选择合适的下载链接。我们通常只在项目的发布目录中托管支持的发布线上的最新稳定版本,而所有之前的 Subversion 版本都可以在 存档 中找到。

要将发布版本上传到 CDN

release.py move-to-dist 1.7.0
这将把 tarball、签名和校验和从 ^/dev/subversion 移动到 ^/release/subversion。内容分发网络 (CDN) 大约需要 15 分钟才能获取到新的发布版本。存档也会自动获取到发布版本。虽然运行 move-to-dist 会移动签名文件,但提交者仍然可以将新的签名提交到 ^/release/subversion;但是,这些签名需要 15 分钟才能出现在 CDN 上。除非在提交签名后 15 分钟内,否则任何此类签名都无法包含在发布公告中。

此时,发布版本可能已公开可用,但最好在 CDN 获取到发布版本之前不要发布公告。在 15 分钟时间过去后,给 CDN 足够的时间同步,发布经理将发送公告并发布对 Subversion 网站的更改,如下所述。

这也是清理 ^/release/subversion 中任何旧发布版本的好时机;该目录中应该只包含每个支持的发布线的最新发布版本。在 ^/release/subversion 中至少可用 24 小时的发布版本将继续保留在存档中。您可以使用以下命令清理旧发布版本

release.py clean-dist

reporter.apache.org 上提交新发布版本的版本号。以下命令

curl -u USERNAME "https://reporter.apache.org/addrelease.py?date=`date +%s`&committee=subversion&version=VERSION&xdate=`date +%F`"
将添加发布版本,它可能应该添加到 release.py 中。

更新网站

即使以下步骤指示直接更新已发布的网站,您也可以在 ^/subversion/site/staging 上准备更改。在这种情况下

  • ^/subversion/site/publish 进行追赶合并。

  • 将任何更改提交到 ^/subversion/site/staging 并在 https://subversion-staging.apache.org 上检查结果。

  • 准备好发布时,将更改合并回 ^/subversion/site/publish(查看合并,以防 staging 上有其他尚未合并的更改)。

对于任何发布版本,包括预发布版本(alpha/beta/rc)

  • 编辑 ^/subversion/site/publish/download.html 以记录最新发布版本。使用 release.py write-downloads 生成表格。如果这是一个稳定版本,请更新 [version][supported] ezt 宏定义(例如 [define version]1.7.0[end]);如果这是一个预发布版本,请取消注释 <div id="pre-releases">;如果这是一个使预发布版本过时的 1.x.0 版本,请将其重新注释。

  • ^/subversion/site/publish/news.html 中添加新的新闻条目,宣布发布。在 ^/subversion/site/publish/index.html 的新闻列表中添加相同的条目,并从该页面中删除最旧的新闻条目。使用 release.py write-news 生成模板新闻条目,然后对其进行自定义。新闻条目中有一个部分应该包含指向公告邮件的链接。目前它是被注释掉的,链接将在稍后添加。如果在发布日期之前生成了模板,请检查日期是否正确。

此外,如果这是一个稳定版本 X.Y.Z(不是 alpha/beta/rc)

  • ^/subversion/site/publish/doap.rdf 中列出新版本

    每个受支持的次要版本应该有一个 <release> 部分,其中 <created> 和 <revision> 更新为当前发布日期和补丁版本号。不要更改文件中的任何其他内容(特别是 <Project> 下的 <created> 是 Subversion 项目创建的日期)。

  • ^/subversion/site/publish/docs/release-notes/release-history.html 中列出新版本

此外,如果这是一个新的次要版本 X.Y.0

  • 更新 ^/subversion/site/publish/docs/release-notes/index.html 的“支持版本”部分中的社区发布支持级别

  • 更新 release.py 中的 supported_release_lines,必要时删除旧行。

  • ^/subversion/site/publish/docs/release-notes/X.Y.html 中删除“草稿”警告

  • ^/site/publish/docs/api/X.Y^/site/publish/docs/javahl/X.Y 中创建或更新版本化的文档快照,并确保这些目录的同级目录中的“latest”符号链接始终指向最新发布系列的目录。

    示例

    VER=1.12
    DOCS_WC=~/src/svn/site/staging/docs
    TAG_BUILD_DIR=~/src/svn/tags/$VER.x/obj-dir
    cd $TAG_BUILD_DIR
    make doc
    cp -a doc/doxygen/html $DOCS_WC/api/$VER
    cp -a doc/javadoc $DOCS_WC/javahl/$VER
    for D in $DOCS_WC/api $DOCS_WC/javahl; do
      svn add $D/$VER
      rm $D/latest && ln -s $VER $D/latest
    done
    svn ci -m "In 'staging': Add $VER API docs." $DOCS_WC/api $DOCS_WC/javahl
    

    更新索引页面 docs/index.html#api 上指向 API 文档的链接。

在发布日期将修改提交到“publish”(或将它们从“staging”合并到“publish”)。

1.x.0 版本的新闻稿

新的次要版本(编号为 1.x.0)可能附带新闻稿。所有关于预期新闻稿的详细信息都在 private@ 列表中处理,并与 [email protected] 协调。

根据经验,在 浸泡开始时 在 private@/press@ 上启动一个线程;最好给 press@ 过长的预警时间,而不是过短的预警时间。

宣布发布

编写一个发布公告,参考之前的公告以获取指导。请务必在公告中包含 URL 和校验和!release.py write-announcement 子命令会创建一个模板公告,可以根据具体情况进行自定义。如果发布修复了安全问题,请传递 --security 标志,以便在输出中生成正确的主题、抄送和描述。

如果社区支持级别随着此版本而改变,请确保在使用它生成公告之前更新 release.py 中的 recommended_release 变量。

从您的 @apache.org 电子邮件地址发送公告。(如果从其他地址发送到 announce@,邮件将被退回。为了获得最佳效果,请按照 提交者电子邮件 页面上的说明操作,并通过官方邮件中继发送您的邮件。)确保您的邮件程序不会将 URL 跨多行换行。

注意:我们在发布公告之前更新网站,以确保发布公告中的所有链接都是有效的。发布公告后,指向发布公告电子邮件的链接将添加到网站中。

发布公告将发布到两个 announce@ 邮件列表:Subversion 项目的 [email protected] 列表和 ASF 范围的 [email protected] 列表。您的邮件可能会被 ASF 范围的 announce@ 列表拒绝。这将生成一个带有主题行的审核通知,例如:Returned post for [email protected]。要求邮件列表软件拒绝邮件的审核员可能忘记在拒绝邮件中签名,从而使拒绝匿名,并且拒绝的理由可能无效。无论如何,保持冷静,并将拒绝转发到 dev@ 邮件列表,以便项目可以讨论是否需要对此采取任何措施。(如有必要,可以联系 announce-owner@ 联系 announce@ 邮件列表审核员。)

更新各种与 Subversion 相关的 IRC 频道中的主题,例如 libera.chat 上的 #svn#svn-dev

如果这是一个 X.Y.0 版本,请更新任何已更改支持状态的分支的最顶部的 STATUS 文件中的社区支持级别。这通常是 X.Y.x/STATUSX.$((Y-1)).x/STATUS,如果新版本是 LTS 版本,那么也是最旧支持的 LTS 分支的 STATUS 文件。

现在是发布经理去享受他最喜欢的饮料的时候了。

创建和维护发布分支

每个新的主要版本和次要版本都会创建一个新的发布分支。例如,在准备发布 2.0.0 版本或 1.3.0 版本时,会创建一个新的发布分支。但是,在准备发布 1.3.1 版本(补丁版本递增)时,会使用在 1.3.0 版本时创建的发布分支。

如果您正在准备发布补丁版本,则无需创建发布分支。您只需从当前次要版本系列发布分支中继续进行。

创建新发布分支的时间点很难确定。通常,我们有一个软性计划,每 6 个月发布一个新的次要版本。因此,在上次发布次要版本后大约 4 个月是一个开始提议分支的好时机。但请记住,这很灵活,取决于正在开发的功能。

准备创建新的次要版本分支

  • 查看 路线图。在分支之前是否需要解决任何未完成的项目?

  • 查看现任稳定分支的 STATUS 文件,了解未完成的 -0 和 -1 票。是否有要包含在新分支中的代码遭到反对?(即使是,也不应该阻止分支,但应该开始讨论,以便在发布分支之前解决问题。)

  • 进行任何树级机械更改。(非机械更改应该在更早的时候完成,以便进行审查。)这将简化以后的合并。例如,去除尾随空格堵塞错误泄漏 以及 修复 C 头文件中的不变式 (另一个案例) 以及 代码搜索和替换。(这也是进行其他定期维护任务的好时机,例如 静态 分析。)

  • 审查新的和更改的 API 的设计和样式(例如,Doxygen 标记、未记录的参数、弃用、公有/私有状态等)。

    abi-laboratory.pro Subversion 可能有用。

  • 运行与现任次要版本 兼容性测试

创建新的次要版本分支

一旦大家同意创建新的发布分支,发布经理将使用以下其中一种流程来创建它(将 A.B 替换为你要准备的版本,例如 1.3 或 2.0)。

使用 release.py 的自动化流程

大多数创建发布分支的工作可以通过 tools/dist/release.py 自动完成。

在一个几乎为空的目录中运行它,它将在其中进行一些临时检出。

release.py --verbose create-release-branch A.B.0

如果之前没有完成,请为项目网站创建发布说明模板。

release.py --verbose write-release-notes A.B.0 > .../docs/release-notes/A.B.html
svn add .../docs/release-notes/A.B.html
svn ci -m "Add release notes template." .../docs/release-notes/A.B.html
release.py 无法自动完成某些步骤,必须手动完成——跳到以下部分的末尾,那里突出显示了这些步骤。

手动流程

本节中的大多数步骤可以通过 tools/dist/release.py 自动完成——见上文——但这里记录了这些步骤,以防发布经理想要手动完成它们。

  • 使用服务器端副本创建新的发布分支。

          svn cp ^/subversion/trunk \
                 ^/subversion/branches/A.B.x \
                 -m "Create the A.B.x release branch."
        
  • 编辑 trunk 上的 subversion/include/svn_version.h 并增加其中的版本号。暂时不要提交这些更改。

    trunk 上的版本号始终反映你刚刚为其创建分支的版本号的下一个主要/次要版本(例如,2.0.x 分支的版本号为 2.1.0,1.3.x 分支的版本号为 1.4.0)。

  • 编辑 trunk 上的 subversion/bindings/javahl/src/org/apache/subversion/javahl/NativeResources.java 并增加 SVN_VER_MINOR。暂时不要提交这些更改。

  • 编辑 trunk 上的 subversion/tests/cmdline/svntest/main.py 并增加 SVN_VER_MINOR。暂时不要提交这些更改。

  • 编辑 trunk 上的 CHANGES 以引入即将发布的新部分(如果尚未存在),以及将来要发布的下一个次要版本的新部分。每个部分以

          Version A.B.0
          (?? ??? 20XX, from /branches/A.B.x)
          https://svn.apache.org/repos/asf/subversion/tags/A.B.0
        

    暂时将发布日期留空。它将一直保持这种状态,直到滚动时间。

  • 提交这些更改,并使用类似以下内容的日志消息

          Increment the trunk version number to A.$((B+1)), and introduce a new CHANGES
          section, following the creation of the A.B.x release branch.
    
          * subversion/include/svn_version.h,
            subversion/bindings/javahl/src/org/apache/subversion/javahl/NativeResources.java,
            subversion/tests/cmdline/svntest/main.py
              (SVN_VER_MINOR): Increment to $((B+1)).
    
          * CHANGES: New section for A.$((B+1)).0.
        
  • 在发布分支上创建一个新的 STATUS 文件。

  • 确保所有 buildbot 构建器都在构建新的发布分支。

    将 A.B 添加到 https://svn.apache.org/repos/infra/infrastructure/buildbot/aegis/buildmaster/master1/projects/subversion.conf 中的 MINOR_LINES 列表中。

  • 创建一个模板发布说明文档,site/publish/docs/release-notes/A.B.html

即使使用 release.py 自动完成大多数其他步骤,以下步骤也必须手动完成。
  • 请具有相应访问权限的人员将 A.B.x 分支添加到 回溯合并机器人 中。

  • 确定发布是常规发布还是 LTS 发布,并在 tools/dist/release-lines.yaml 中的 lts_release_lines 中为 release.py 记录该决定。

管理 Backport 合并机器人

Backport 合并机器人每天晚上在 svn-qavm1 机器上运行,在 svnsvn 本地用户帐户下,使用 svn-role Subversion 帐户名进行提交。

此配置目前需要具有机器管理员权限的人员来管理分支集的更改。

该机器人将批准的 backport 合并到每个分支 A.B.x 上,这些分支在 ~svnsvn/src/svn/A.B.x 中存在 WC 目录。

那里的签出是通过运行(作为 svnsvn 用户,例如使用 sudo)类似以下内容来创建的

svn checkout --depth=empty https://svn-master.apache.org/repos/asf/subversion/branches ~svnsvn/src/svn
svn up ~svnsvn/src/svn/A.B.x  # for each supported branch

仓库 URL 是 svn-master.a.o,因为 backport 合并器在循环中运行 svn ci && svn up,并假设 svn up 会将其更新到它刚刚提交的修订版。当从镜像更新时,这并不保证(并且 svn.a.o 可能指向镜像)。构建机器人从属节点也出于同样的原因这样做。

每个分支都在 WC 根目录的子目录中。要添加新分支,请使用 svn up 填充源树

sudo -H -u svnsvn  svn up ~svnsvn/src/svn/A.B.x

要删除不再支持的分支,请使用 svn up -r0

sudo -H -u svnsvn  svn up -r0 ~svnsvn/src/svn/Z.Z.x

有关设置的更多说明,请参见 machines/svn-qavm1/ 和 Subversion PMC 的 私有仓库。在 machines/ 的历史记录中,还有一些关于先前版本的历史记录。

将更改移植到发布分支

创建发布分支后,将永远不会在那里进行开发。唯一允许的更改是针对各种簿记文件(如 STATUS)所做的更改,以及从主干合并的更改。在极少数情况下,可以从 A.B.x 分支创建特性分支来解决特定于发布分支的问题(例如,修复不影响主干的错误)。

用于接受或拒绝从主干合并更改的协议对所有 Subversion 开发人员都很有趣,因此在 发布稳定部分 中有记录。

管理 CHANGES 文件

CHANGES 文件是项目的变更日志文件。在发布之前,必须将其更新以列出自上次发布以来的所有更改。

下面,我们将描述手动流程。有关部分自动化的信息,请参见

release.py write-changelog

对于补丁发布,这相当容易:您只需要遍历自上次“黄金”修订版以来的分支的提交日志,并记录所有有趣的合并。对于次要和主要发布,这更复杂:您需要遍历自上次发布分支分叉以来的主干上的提交日志,并记录所有更改。过程相同,但要长得多,而且有些复杂,因为它涉及过滤掉已经 backport 到以前的发布分支并从那里发布的变更集。

请记住,CHANGES 应该始终在主干分支上进行编辑,然后在必要时合并到发布分支。非常重要的一点是,所有版本的更改都应记录在主干分支的 CHANGES 文件中,以便将来参考,并确保未来的发布分支包含所有先前更改日志的总和。

保持每个更改的项目符号简洁,最好不超过一行。有时这可能是一个挑战,但它确实提高了文档的整体可读性。问问自己:如果描述需要超过一行,也许我太详细了?

为分支编写初始内容

运行 svn log --stop-on-copy ^/subversion/branches/A.B.x。这将显示对 A.B.x 分支的所有更改,包括回退到 A.B.x 分支的更改。然后,您需要删除先前主要/次要分支的微版本中已发布的更改日志。在先前发布分支上运行 svn log -q --stop-on-copy,然后编写一个脚本解析版本号并将其从主日志输出中删除。(Karl 和 Ben 过去使用 emacs 宏来完成此操作,但建议我们编写一个更通用的脚本。)(更新:现在,svn mergeinfo --show-revs eligible 应该简化获取相关修订集的过程——使用次要分支作为源,使用先前的次要分支作为目标。)

从最旧到最新的顺序阅读该日志,在阅读时总结要点。诀窍是知道要写多少细节:您不想提及每个微小的提交,但也不想过于笼统。在开始新部分之前,先阅读几页 CHANGES 文件,以设置您的过滤级别,以保持一致性。

为补丁版本添加内容

作为 发布稳定性 的一部分,应在将错误修复移植到发布分支时更新 CHANGES。通常,如果您将修订版或修订版组(即 STATUS 中的项目)合并到发布分支,您还应该在主干分支的 CHANGES 中添加一个项目,遵循上面概述的相同指南。然后,在发布补丁版本时,此列表将合并到发布分支。

实际上,CHANGES 不会在每次将修复程序回退到发布分支时都更新。通常,发布经理会将更新 CHANGES 作为发布补丁版本的第一个步骤之一。

获取应在 CHANGES 中提及的回退列表的便捷方法是使用与填充 Subversion 网站上的 即将发布的补丁版本 相同的工具。这是 https://svn.apache.org/repos/asf/subversion/site/tools 中的 upcoming.py 脚本。在当前工作目录为次要分支的工作副本根目录时运行它。

如何发布 Subversion 版本

要了解 Subversion 1.5 发布周期中出现的错误案例,请参阅 这篇论文