本文本尚在编写中,内容可能会发生很大变化,并且可能无法准确描述任何已发布版本的 Apache™ Subversion® 软件。将此页面添加书签或以其他方式向他人推荐此页面可能不是明智之举。请访问 https://svnbook.subversion.org.cn/ 以获取本书的稳定版本。
此时,您应该了解每次提交如何在存储库中创建文件系统树的新状态(称为 “修订版”)。如果您不了解,请返回阅读 名为“修订版”的部分。
让我们重新审视 第 1 章,基本概念 中的示例。请记住,您和您的合作者 Sally 共享一个存储库,其中包含两个项目,paint
和 calc
。请注意,在 图 4.2,"开始的存储库布局" 中,每个项目目录现在都包含名为 trunk
和 branches
的子目录。这样做的原因很快就会变得清晰。
如前所述,假设您和 Sally 都拥有 “calc” 项目的工作副本。具体来说,你们每个人都拥有 /calc/trunk
的工作副本。项目的所有文件都位于此子目录中,而不是位于 /calc
本身,因为您的团队已决定 /calc/trunk
是进行 “主线” 开发的位置。
假设您被分配了实施大型软件功能的任务。这需要很长时间才能编写,并且会影响项目中的所有文件。眼前的问题是,您不想干扰 Sally,她正在修复这里和那里的少量错误。她依赖于项目最新版本(在 /calc/trunk
中)始终可用这一事实。如果您开始逐个提交您的更改,您一定会破坏 Sally(以及其他团队成员)的工作。
一种策略是躲起来:您可以停止共享信息一两周,在您的私有工作副本中彻底修改和重组所有文件,但在完成任务之前不提交或更新。不过,这有很多问题。首先,它很不安全。如果您的工作副本或计算机出现问题,您可能会丢失所有更改。其次,它不灵活。除非您手动在不同的工作副本或计算机上复制您的更改,否则您只能在一个工作副本中进行更改。同样,很难与其他人共享您的工作进度。常见的软件开发 “最佳实践” 是允许您的同行在您进行更改时查看您的工作。如果没有人看到您的中间提交,您可能会失去潜在的反馈,并且可能会在您的团队中的其他人注意到问题之前,在错误的路径上走几周。最后,当您完成所有更改后,您可能会发现将完成的工作与公司代码库的主体合并非常困难。Sally(或其他人)可能在存储库中进行了许多其他更改,这些更改在您最终运行 svn update 之后几周的隔离后,很难合并到您的工作副本中。
更好的解决方案是在存储库中创建您自己的分支,或开发线。这使您能够经常保存尚未完成的工作,而不会干扰他人的更改,同时仍然可以选择性地与您的合作者共享信息。我们将继续说明这如何工作。
创建分支非常简单 - 您使用 svn copy 命令在存储库中复制项目树。由于项目的源代码位于 /calc/trunk
目录中,因此您将复制该目录。新副本应该放在哪里?您可以将其放在任何您希望的位置。存储库中存放分支的位置由 Subversion 作为项目策略来决定。最后,您的分支将需要一个名称来区分它与其他分支。同样,您选择的名称对 Subversion 并不重要 - 您可以使用对您和您的团队最有效的任何名称。
假设您的团队(像大多数团队一样)有一项策略,即在项目主干的兄弟目录 branches
目录中创建分支(在我们这个场景中,即 /calc/branches
目录)。由于缺乏灵感,您将 my-calc-branch
作为您希望为分支命名的名称。这意味着您将创建一个新目录,/calc/branches/my-calc-branch
,它最初是 /calc/trunk
的副本。
您可能已经看到 svn copy 用于将一个文件复制到工作副本中的另一个文件。但它也可以用于进行 远程复制 - 立即导致新提交的存储库修订版且根本不需要工作副本的复制。只需将一个 URL 复制到另一个 URL
$ svn copy ^/calc/trunk ^/calc/branches/my-calc-branch \ -m "Creating a private branch of /calc/trunk." Committed revision 341. $
此命令会导致存储库中立即进行提交,在修订版 341 中创建一个新目录。新目录是 /calc/trunk
的副本。这在 图 4.3,"具有新副本的存储库" 中显示。[32] 虽然也可以使用 svn copy 在工作副本中复制目录来创建分支,但这项技术并不推荐。实际上,它可能非常慢!在客户端复制目录是线性时间操作,因为它实际上必须在本地磁盘上复制该工作副本目录中的每个文件和子目录。然而,在服务器上复制目录是恒定时间操作,并且是大多数人创建分支的方式。此外,这种做法会增加复制混合修订版工作副本的可能性。这本身并不危险,但可能会在以后的合并过程中造成不必要的复杂性。如果您确实选择通过复制工作副本路径来创建分支,您应该确保源目录没有本地修改,并且不在混合修订版中。
现在您已经创建了项目的某个分支,您可以签出新的工作副本以开始使用它
$ svn checkout http://svn.example.com/repos/calc/branches/my-calc-branch A my-calc-branch/doc A my-calc-branch/src A my-calc-branch/doc/INSTALL A my-calc-branch/src/real.c A my-calc-branch/src/main.c A my-calc-branch/src/button.c A my-calc-branch/src/integer.c A my-calc-branch/Makefile A my-calc-branch/README Checked out revision 341. $
这个工作副本没有什么特别之处;它只是镜像了存储库中的另一个目录。但是,当您提交更改时,Sally 在更新时不会看到这些更改,因为她的工作副本是 /calc/trunk
。(请务必阅读本章后面的 名为“遍历分支”的部分:svn switch 命令是创建分支工作副本的另一种方法。)
假设经过了一周,并发生了以下提交
您对 /calc/branches/my-calc-branch/src/button.c
进行更改,这会创建修订版 342。
您对 /calc/branches/my-calc-branch/src/integer.c
进行更改,这会创建修订版 343。
Sally 对 /calc/trunk/src/integer.c
进行更改,这会创建修订版 344。
现在,在 integer.c
上发生了两条独立的开发线(如 图 4.4,"单个文件的历史记录分支" 中所示)。
当您查看对 integer.c
的副本所做的更改历史记录时,事情变得有趣了
$ pwd /home/user/my-calc-branch $ svn log -v src/integer.c ------------------------------------------------------------------------ r343 | user | 2013-02-15 14:11:09 -0500 (Fri, 15 Feb 2013) | 1 line Changed paths: M /calc/branches/my-calc-branch/src/integer.c * integer.c: frozzled the wazjub. ------------------------------------------------------------------------ r341 | user | 2013-02-15 07:41:25 -0500 (Fri, 15 Feb 2013) | 1 line Changed paths: A /calc/branches/my-calc-branch (from /calc/trunk:340) Creating a private branch of /calc/trunk. ------------------------------------------------------------------------ r154 | sally | 2013-01-30 04:20:03 -0500 (Wed, 30 Jan 2013) | 2 lines Changed paths: M /calc/trunk/src/integer.c * integer.c: changed a docstring. ------------------------------------------------------------------------ … ------------------------------------------------------------------------ r113 | sally | 2013-01-26 15:50:21 -0500 (Sat, 26 Jan 2013) | 2 lines Changed paths: M /calc/trunk/src/integer.c * integer.c: Revise the fooplus API. ------------------------------------------------------------------------ r8 | sally | 2013-01-17 16:55:36 -0500 (Thu, 17 Jan 2013) | 1 line Changed paths: A /calc/trunk/Makefile A /calc/trunk/README A /calc/trunk/doc/INSTALL A /calc/trunk/src/button.c A /calc/trunk/src/integer.c A /calc/trunk/src/main.c A /calc/trunk/src/real.c Initial trunk code import for calc project. ------------------------------------------------------------------------
请注意,Subversion 正在跟踪分支 integer.c
的历史记录,一直追溯到过去,甚至遍历了复制它的点。它将分支的创建显示为历史记录中的一个事件,因为在复制整个 /calc/trunk/
时,integer.c
被隐式复制。现在看看当 Sally 对文件副本运行相同的命令时会发生什么
$ pwd /home/sally/calc $ svn log -v src/integer.c ------------------------------------------------------------------------ r344 | sally | 2013-02-15 16:44:44 -0500 (Fri, 15 Feb 2013) | 1 line Changed paths: M /calc/trunk/src/integer.c Refactor the bazzle functions. ------------------------------------------------------------------------ r154 | sally | 2013-01-30 04:20:03 -0500 (Wed, 30 Jan 2013) | 2 lines Changed paths: M /calc/trunk/src/integer.c * integer.c: changed a docstring. ------------------------------------------------------------------------ … ------------------------------------------------------------------------ r113 | sally | 2013-01-26 15:50:21 -0500 (Sat, 26 Jan 2013) | 2 lines Changed paths: M /calc/trunk/src/integer.c * integer.c: Revise the fooplus API. ------------------------------------------------------------------------ r8 | sally | 2013-01-17 16:55:36 -0500 (Thu, 17 Jan 2013) | 1 line Changed paths: A /calc/trunk/Makefile A /calc/trunk/README A /calc/trunk/doc/INSTALL A /calc/trunk/src/button.c A /calc/trunk/src/integer.c A /calc/trunk/src/main.c A /calc/trunk/src/real.c Initial trunk code import for calc project. ------------------------------------------------------------------------
Sally 看到她自己的修订版 344 更改,但没有看到您在修订版 343 中做出的更改。就 Subversion 而言,这两个提交影响了不同存储库位置中的不同文件。但是,Subversion 确实 显示这两个文件共享一个共同的历史记录。在修订版 341 中进行分支复制之前,这两个文件曾经是同一个文件。这就是您和 Sally 都看到修订版 8 到 154 之间发生的更改的原因。
您应该记住本节中的两个重要经验教训。首先,Subversion 没有分支的内部概念 - 它只知道如何进行复制。当您复制目录时,结果目录只是 “分支”,因为 您 将该含义附加到它。您可能以不同的方式看待该目录,或者以不同的方式对待它,但对 Subversion 来说,它只是一个普通的目录,碰巧带有一些额外的历史信息。
其次,由于这种复制机制,Subversion 的分支存在于存储库中的 正常文件系统目录 中。这与其他版本控制系统不同,在其他版本控制系统中,分支通常通过向文件集合添加额外的维度 “标签” 来定义。分支目录的位置对 Subversion 并不重要。大多数团队遵循将所有分支放在 /branches
目录中的约定,但您可以自由地创建任何您想要的策略。