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

概述

参与社区

虽然 Subversion 最初是由 CollabNet 赞助和托管的 (https://www.collab.net/),但它是一个真正的开源项目,根据 Apache 许可证 2.0 版授权。Subversion 的许多开发人员由他们的雇主付费来改进 Subversion,而许多其他人只是优秀的志愿者,他们有兴趣构建一个更好的版本控制系统。

社区主要通过 IRC、邮件列表和 Subversion 存储库存在。要参与

多年来,Subversion 社区每年在柏林举行一次黑客马拉松:2016201520142013201220112010 [链接来自 archive.org,一些媒体链接无法使用]。

有很多方法可以加入项目,无论是编写代码,还是测试和/或帮助管理错误数据库。如果您想贡献,请查看

要提交代码,只需将您的补丁发送到 [email protected]。不,等等,先阅读本文档的其余部分,然后开始将补丁发送到 [email protected]。:-)

要帮助管理问题数据库,请查看问题摘要,查找和测试无效或重复其他问题的问题。这两种情况都很常见,第一种情况是因为错误通常在代码中其他更改的副作用中被不知不觉地修复,第二种情况是因为人们有时会提交问题而没有注意到它已经被报告过。如果您不确定某个问题,请在 [email protected] 上发布问题。(“Subversion:我们在这里帮助您帮助我们!”)

另一种帮助方法是在某些平台上设置 Subversion 的自动化构建和测试套件运行,并将输出发送到 [email protected] 邮件列表。有关更多详细信息,请参见 邮件列表页面

最后,尽管 Subversion 项目的本质是线上的,并且由此产生的用户接触抽象化,但重要的是要意识到,所有贡献的背后都是真实的人。对待所有其他社区成员的方式,就如同你希望别人对待你一样。审查贡献,而不是贡献者;不要惹恼别人,也不要轻易被惹恼。

理论和文档

  1. 设计

    2000 年 6 月编写了一份设计规范,现在已经过时了。但它仍然很好地介绍了存储库的内部工作原理以及 Subversion 的各个层。

  2. API 文档

    有关更多信息,请参见关于公共 API 文档的部分。

  3. 增量编辑器

    Karl Fogel 为 O'Reilly 2007 年的书籍Beautiful Code: Leading Programmers Explain How They Think撰写了一章,涵盖了Subversion 的增量编辑器接口的设计和使用。

  4. 网络协议

    WebDAV 使用文档介绍了 Subversion 的 DAV 网络协议,该协议是 HTTP 的扩展方言,使用以“http://”或“https://”开头的 URL。

    SVN 协议文档包含了 Subversion ra_svn 网络协议的正式描述,该协议是端口 3690(默认情况下)上的自定义协议,其 URL 以“svn://”或“svn+ssh://”开头。

  5. 用户手册

    Version Control with Subversion 是 O'Reilly 出版的一本书,详细介绍了如何有效地使用 Subversion。本书的文本是免费的,并且正在积极修订。在线版本可在https://svnbook.subversion.org.cn/获取。XML 源代码和其他语言的翻译在https://sourceforge.net/projects/svnbook/的自己的存储库中维护。

  6. 系统说明

    系统特定方面的许多设计理念都记录在notes/目录中的各个文件中。

要阅读的代码

在贡献代码之前,你需要熟悉现有的代码库和接口。

检出 Subversion 的副本(如果您还没有提交访问权限的帐户,可以匿名检出)——这样您就可以查看代码。

在 'subversion/include/' 目录中,有一堆带有大量文档注释的头文件。如果您通读这些文件,您将对实现细节有相当好的了解。以下是一个建议的阅读顺序:

  1. 基本构建块: svn_string.h svn_error.h svn_types.h

  2. svn_io.h svn_path.h svn_hash.h svn_xml.h

  3. 关键接口: svn_delta.h

  4. 客户端接口: svn_ra.h svn_wc.h svn_client.h

  5. 仓库和版本化文件系统: svn_repos.h svn_fs.h

Subversion 试图通过仅使用 C89/C90ANSI/ISO C 方言以及使用 Apache Portable Runtime (APR) 库来保持可移植性。APR 是 Apache httpd 服务器使用的可移植性层,您可以在 https://apr.apache.org/ 上找到更多信息。

由于 Subversion 严重依赖 APR,因此在不先浏览 APR 中的某些头文件(位于 'apr/include/' 中)的情况下,可能难以理解 Subversion。

Subversion 还试图提供可靠且安全的软件。这只有在开发人员了解 C 编程语言中的安全编程的情况下才能实现。请参阅 'notes/assurance.txt' 以了解其背后的完整理由。具体来说,您应该认真阅读 David Wheeler 的《安全编程》(如 'notes/assurance.txt' 中所述)。如果您在任何时候对更改的安全影响有任何疑问,请在开发者邮件列表中寻求审查。

目录布局

源代码树粗略指南

分支策略

Subversion 项目强烈建议在公共主干上进行活跃的开发。对主干进行的更改具有最高的可见度,并且可以从未发布的代码中获得最大的锻炼。为了使这有利于所有人,我们的主干始终应保持稳定。它应该构建。它应该工作。它可能还没有准备好发布,但它肯定应该准备好进行测试。

我们也强烈建议将大型更改分解成几个较小的逻辑提交,每个提交都应满足上述稳定性要求。

也就是说,我们理解,对于特别大的更改(新功能、大规模代码重构等),几乎不可能应用所有这些策略。在这种情况下,您可能需要考虑使用专门用于您的开发任务的自定义分支。以下是一些使您的基于分支的开发工作顺利进行的指南。

分支创建和管理

基于分支的开发并没有特别复杂。您从主干(或从最适合作为您的工作来源和目标的分支)创建一个分支,并在该分支上进行工作。Subversion 的合并跟踪功能极大地帮助减少了以这种方式工作所需的思维负担,因此强烈建议您充分利用该功能(通过使用 Subversion 1.5 或更新的客户端,并对分支的根目录执行所有合并)。

有关分支日志消息的策略,请注意有关 编写日志消息 的部分。

轻量级分支

如果您正在分阶段进行功能或错误修复,涉及多个提交,并且某些中间阶段不稳定到无法合并到主干,那么请在 /branches 中创建一个临时分支。无需询问——直接创建即可。在临时分支中尝试实验性想法也是可以的。所有上述内容适用于部分提交者和完整提交者。它甚至适用于其他 ASF 项目的提交者,但请与我们联系(在 dev@ 上)——介绍您自己以及您计划处理的问题。

完成分支后——无论是将其合并到主干还是放弃它——请记住将其删除。

另请参阅 关于部分提交访问权限的部分,了解我们关于为实验性分支提供提交访问权限的政策。

BRANCH-README 文件

对于您预期将长期存在的分支,我们建议在分支根目录中创建一个名为 BRANCH-README 的文件,并定期更新该文件。此类文件为您提供了一个绝佳的中心位置来描述分支的以下方面

  • 分支的基本目的:它存在于修复哪个错误或实现哪个功能;它与哪些问题编号相关;哪些列表讨论线程围绕它;哪些设计文档存在于描述这种情况。

  • 您正在使用哪种分支管理风格:这是一个功能分支,它将定期与父分支保持同步,并最终重新集成回该父分支?它是一个预计在可预见的将来不会合并回其父分支的分支?它是否与任何其他分支相关?

  • 您在分支上还有哪些任务要完成?这些任务是否有人认领?他们是否需要更多设计输入?其他人如何帮助您?

这是一个示例 BRANCH-README 文件,演示了我们正在讨论的内容

This branch exists for the resolution of issue #8810, per the ideas
documented in /trunk/notes/frobnobbing-feature.txt.  It is a feature
branch, receiving regular sync merges from /trunk, and expected to be
reintegrated back thereto.

TODO:

  * compose regression tests        [DONE]
  * add frob identification logic   [STARTED (fitz)]
  * add nobbing bits                []

为什么大惊小怪?因为这个项目理想化了沟通和协作,理解后者更有可能发生在将前者作为重点的情况下。

只需记住,当您将分支合并回其源代码时,请删除 BRANCH-README 文件。

文档

记录一切

每个函数,无论是公共的还是内部的,都必须以一个文档注释开头,描述函数的功能。文档应提及函数接收的每个参数、每个可能的返回值,以及(如果不明确)函数可能返回错误的条件。

对于内部文档,在文档字符串中将参数名称大写,即使在实际声明中它们不是大写,这样它们对人类读者来说更突出。

对于公共或半公共 API 函数,文档字符串应放在 .h 文件中声明函数的上方;否则,它应放在 .c 文件中函数定义的上方。

对于结构类型,请记录结构本身的每个成员。

对于实际的源代码,在内部记录每个函数的代码块,以便熟悉 Subversion 的人能够理解正在实现的算法。不要包含明显或过于冗长的文档;注释应该帮助理解代码,而不是阻碍它。

例如

  /*** How not to document.  Don't do this. ***/

  /* Make a foo object. */
  static foo_t *
  make_foo_object(arg1, arg2, apr_pool_t *pool)
  {
     /* Create a subpool. */
     apr_pool_t *subpool = svn_pool_create(pool);

     /* Allocate a foo object from the main pool */
     foo_t *foo = apr_palloc(pool, sizeof(*foo));
     ...
  }

相反,记录相当大的代码块,像这样

      /* Transmit the segment (if its within the scope of our concern). */
      SVN_ERR(maybe_crop_and_send_segment(segment, start_rev, end_rev,
                                          receiver, receiver_baton, subpool));

      /* If we've set CURRENT_REV to SVN_INVALID_REVNUM, we're done
         (and didn't ever reach END_REV).  */
      if (! SVN_IS_VALID_REVNUM(current_rev))
        break;

      /* If there's a gap in the history, we need to report as much
         (if the gap is within the scope of our concern). */
      if (segment->range_start - current_rev < 1)
        {
          svn_location_segment_t *gap_segment;
          gap_segment = apr_pcalloc(subpool, sizeof(*gap_segment));
          gap_segment->range_end = segment->range_start - 1;
          gap_segment->range_start = current_rev + 1;
          gap_segment->path = NULL;
          SVN_ERR(maybe_crop_and_send_segment(gap_segment, start_rev, end_rev,
                                              receiver, receiver_baton,
                                              subpool));
        }

阅读 Subversion 代码以了解文档在实践中的外观;特别是,请参阅 subversion/include/*.h 以获取 doxygen 示例。

公共 API 文档

我们使用 Doxygen 格式来记录公共接口。这意味着任何在公共头文件中出现的内容。生成的文档在 网站上发布,用于最新的和一些早期的 Subversion 源代码。

我们只使用一小部分可用的 doxygen 命令 来标记我们的源代码。在编写 doxygen 文档时,以下约定适用

  • 使用完整的句子和函数的散文描述,在参数名称前加 @a,在类型和宏名称前加 @c
  • 使用 <tt>...</tt> 来显示多个单词,使用 @p 来显示打字机字体中的单个单词。
  • 常量值,如 TRUEFALSENULL 应全部大写。
  • 当多个函数相关时,定义一个组名,并使用 @defgroup@{...@} 将它们分组在一起。

有关命令的完整列表,请参阅 Doxygen 手册

补丁提交指南

编写补丁

要获取最新源代码,请运行

svn checkout https://svn.apache.org/repos/asf/subversion/trunk/ svn-trunk

并按照 INSTALL 文件中的说明操作。(如果您没有 svn 客户端,请 下载源代码包。)

如果您的补丁实现了新功能或更改了大量代码,请记住先在 dev@ 邮件列表中讨论。(IRC 上的 #svn-dev 也是在邮件列表讨论之前(而不是代替)快速反馈的合适场所。如果您在 IRC 上提问,请等待一段时间以获得回复,因为并非所有人一直在线(尤其是在周末)。)这样社区就可以尽早表达对所提议功能或实现细节的担忧并提出改进建议——如果这些反馈越早提供(甚至在编写任何代码之前)而不是之后,对所有各方来说总是更好。

如果您对补丁有任何疑问,请随时在 IRC 或 dev@ 上提问。

提交补丁

将补丁邮件发送到 [email protected],主题行以 [PATCH] 开头。这有助于我们的 补丁管理员 立即发现补丁。例如

   Subject: [PATCH] fix for rev printing bug in svn status

如果补丁解决了特定问题,请也包含问题编号:“[PATCH] issue #1729: ...”。对该特定问题感兴趣的开发人员将知道阅读邮件。

补丁提交应包含一个逻辑更改;请不要在一个提交中混合 N 个不相关的更改——而是发送 N 封单独的电子邮件。

使用 svn diff -x-p 从 Subversion 主干工作副本的顶部生成补丁。如果您要 diff 的文件不在版本控制之下,您可以使用 diff -u 达到相同的效果。

请在您的补丁中包含一个日志消息。一个好的日志消息有助于潜在的审阅者理解您补丁中的更改,并增加它被应用的可能性。您可以将日志消息放在电子邮件正文中,也可以放在补丁附件的顶部(见下文)。无论哪种方式,它都应遵循 编写日志消息 中给出的指南,并用三个方括号括起来,如下所示

   [[[
   Fix issue #1729: Don't crash because of a missing file.

   * subversion/libsvn_ra_ansible/get_editor.c
     (frobnicate_file): Check that file exists before frobnicating.
   ]]]

(方括号实际上不是日志消息的一部分,它们只是用来清楚地将日志消息与其周围的上下文区分开来。)

如果可能,请将补丁作为附件发送,其 mime 类型为 text/x-difftext/x-patchtext/plain。大多数人的邮件阅读器可以内联显示这些内容,并且将补丁作为附件可以让它们方便地从消息中提取补丁。切勿以存档或压缩形式(例如,tar、gzip、zip、bzip2)发送补丁,因为这会阻止人们直接在他们的邮件阅读器中查看补丁。

如果您无法使用这些 MIME 类型之一附加补丁,或者补丁非常短,那么可以直接将其包含在邮件正文中。但请注意:某些邮件编辑器会通过在长行的中间插入不需要的换行符来修改内联补丁。如果您认为您的邮件软件可能会这样做,请使用附件。

如果补丁实现了新功能,请确保在邮件中完整描述该功能;如果补丁修复了错误,请详细描述错误并提供重现步骤。这些指南的例外情况是,当补丁解决问题数据库中的特定问题时,在这种情况下,只需在您的日志消息中引用问题编号,如 编写日志消息 中所述。

补丁经过几轮反馈和更改后才能应用是正常的。如果您的补丁没有立即被接受,不要灰心,这并不意味着您犯了错误,只是意味着有 *很多* 双眼睛在查看每个代码提交,很少有补丁不需要至少进行一点改进。在讨论了人们对您的补丁的反馈后,进行适当的更改并重新提交,等待下一轮反馈,然后重复此过程,直到某个提交者应用它。您可以在提交补丁之前,通过查看和应用项目的 编码规范 来避免一些迭代。

如果您一段时间没有收到回复,并且没有看到补丁被应用,这可能只是意味着人们真的很忙。继续重新发布,不要犹豫指出您仍在等待回复。您可以这样想,补丁管理是高度可并行的,我们需要您承担管理和编码的份额。每个补丁都需要有人来引导它完成整个过程,而最适合做这件事的人就是最初的提交者。