本手册旨在描述 Subversion 1.1。如果您运行的是更新版本的 Subversion,我们强烈建议您访问 https://svnbook.subversion.org.cn/ 并查阅适合您 Subversion 版本的书籍。

使用分支

此时,您应该理解每次提交如何在存储库中创建一个全新的文件系统树(称为“修订版”)。如果没有,请返回阅读关于 “修订版”部分的内容。

在本节中,我们将回到第 2 章中的同一个示例。请记住,您和您的合作者莎莉共享了一个包含两个项目的存储库,paintcalc。请注意,在 图 4.2,“存储库初始布局”中,每个项目目录现在都包含名为trunkbranches的子目录。原因将在稍后说明。

图 4.2:存储库初始布局

Starting repository layout

如前所述,假设您和莎莉都拥有“calc”项目的本地副本。具体来说,你们每个人都拥有/calc/trunk的本地副本。项目的所有文件都位于此子目录中,而不是位于/calc本身,因为您的团队已决定/calc/trunk是进行“主线”开发的地方。

假设您被分配了对项目进行彻底重组的任务。这将需要很长时间才能完成,并且会影响项目中的所有文件。这里的问题在于,您不想干扰莎莉,她正在修复一些零散的小错误。她依赖于项目最新版本(位于/calc/trunk)始终可用。如果您开始逐个提交更改,您肯定会破坏莎莉的工作。

一种策略是躲起来:您和莎莉可以停止信息共享一两周。也就是说,开始彻底修改您本地副本中的所有文件,但在完成任务之前不要提交或更新。但是,这种策略存在很多问题。首先,它不太安全。大多数人喜欢经常将他们的工作保存到存储库中,以防本地副本意外发生损坏。其次,它不太灵活。如果您在不同的计算机上进行工作(也许您在两台不同的计算机上都拥有/calc/trunk的本地副本),您需要手动来回复制更改,或者只在一台计算机上完成所有工作。同样,与其他人共享您的正在进行的更改也很困难。常见的软件开发“最佳实践”是允许您的同行在您进行更改时审查您的工作。如果没有人看到您的中间提交,您将失去潜在的反馈。最后,当您完成所有更改后,您可能会发现将最终结果重新合并到公司的主要代码库中非常困难。莎莉(或其他人)可能在存储库中做了许多其他更改,这些更改难以合并到您的本地副本中,尤其是如果您在隔离几周后运行 svn update

更好的解决方案是在存储库中创建自己的分支或开发线。这样,您可以经常保存尚未完成的工作,而不会干扰他人,同时您仍然可以有选择地与您的合作者共享信息。您将在稍后看到这到底是如何实现的。

创建分支

创建分支非常简单,您只需使用 svn copy 命令在存储库中复制项目。Subversion 不仅可以复制单个文件,还可以复制整个目录。在这种情况下,您需要复制/calc/trunk目录。新副本应该放在哪里?您可以选择任何地方,这取决于项目策略。假设您的团队有一个创建分支到/calc/branches存储库区域的策略,并且您想将您的分支命名为my-calc-branch。您需要创建一个新目录,/calc/branches/my-calc-branch,它最初是一个/calc/trunk.

的副本。您可以通过两种不同的方式来进行复制。首先,我们将演示比较麻烦的方式,以便更好地理解概念。首先,检出一个项目根目录的本地副本,/calc:

$ svn checkout http://svn.example.com/repos/calc bigwc
A  bigwc/trunk/
A  bigwc/trunk/Makefile
A  bigwc/trunk/integer.c
A  bigwc/trunk/button.c
A  bigwc/branches/
Checked out revision 340.

现在,只需将两个本地副本路径传递给 svn copy 命令,就可以进行复制。

$ cd bigwc
$ svn copy trunk branches/my-calc-branch
$ svn status
A  +   branches/my-calc-branch

在本例中,svn copy 命令将trunk工作目录递归复制到一个新的工作目录中,branches/my-calc-branch。从 svn status 命令的输出可以看出,新目录现在已计划添加到存储库中。但是请注意字母 A 旁边的“+”符号。这表示计划的添加是 副本,而不是新内容。当您提交更改时,Subversion 将通过复制/calc/branches/my-calc-branch来在存储库中创建/calc/trunk,而不是通过网络重新发送所有本地副本数据。

$ svn commit -m "Creating a private branch of /calc/trunk."
Adding         branches/my-calc-branch
Committed revision 341.

现在,让我们介绍一种更简单的方法来创建分支,我们应该一开始就告诉你:svn copy 可以直接操作两个 URL。

$ svn copy http://svn.example.com/repos/calc/trunk \
           http://svn.example.com/repos/calc/branches/my-calc-branch \
      -m "Creating a private branch of /calc/trunk."

Committed revision 341.

这两种方法之间并没有真正的区别。这两个过程都在修订版 341 中创建一个新目录,并且新目录是/calc/trunk的副本。这在 图 4.3,“带有新副本的存储库”中有所体现。请注意,第二种方法执行的是 立即 提交。 [7] 这是一个更简单的过程,因为它不需要您检出存储库的大型镜像。实际上,这种技术甚至不需要您拥有本地副本。

图 4.3:带有新副本的存储库

Repository with new copy

使用您的分支

现在您已经创建了项目的副本,您可以检出一个新的本地副本并开始使用它。

$ svn checkout http://svn.example.com/repos/calc/branches/my-calc-branch
A  my-calc-branch/Makefile
A  my-calc-branch/integer.c
A  my-calc-branch/button.c
Checked out revision 341.

这个本地副本没什么特别之处,它只是反映了存储库中的另一个目录。但是,当您提交更改时,莎莉在更新时将永远看不到它们。她的本地副本是/calc/trunk。(请务必阅读本章后面的 “切换本地副本”部分svn switch 命令是创建分支本地副本的另一种方式。)

假设时间过去了,发生了一周的提交。

  • 您对/calc/branches/my-calc-branch/button.c进行了更改,这创建了修订版 342。

  • 您对/calc/branches/my-calc-branch/integer.c进行了更改,这创建了修订版 343。

  • 莎莉对/calc/trunk/integer.c进行了更改,这创建了修订版 344。

现在有两个独立的开发线,如 图 4.4,“单个文件历史记录的分支”所示,它们正在integer.c.

上进行开发。

The branching of one file's history

图 4.4:单个文件历史记录的分支integer.c:

$ pwd
/home/user/my-calc-branch

$ svn log --verbose integer.c
------------------------------------------------------------------------
r343 | user | 2002-11-07 15:27:56 -0600 (Thu, 07 Nov 2002) | 2 lines
Changed paths:
   M /calc/branches/my-calc-branch/integer.c

* integer.c:  frozzled the wazjub.

------------------------------------------------------------------------
r341 | user | 2002-11-03 15:27:56 -0600 (Thu, 07 Nov 2002) | 2 lines
Changed paths:
   A /calc/branches/my-calc-branch (from /calc/trunk:340)

Creating a private branch of /calc/trunk.

------------------------------------------------------------------------
r303 | sally | 2002-10-29 21:14:35 -0600 (Tue, 29 Oct 2002) | 2 lines
Changed paths:
   M /calc/trunk/integer.c

* integer.c:  changed a docstring.

------------------------------------------------------------------------
r98 | sally | 2002-02-22 15:35:29 -0600 (Fri, 22 Feb 2002) | 2 lines
Changed paths:
   M /calc/trunk/integer.c

* integer.c:  adding this file to the project.

------------------------------------------------------------------------

当您查看对您自己的integer.c副本进行的更改历史记录时,事情变得很有趣。integer.c请注意,Subversion 正在跟踪您分支的的历史记录,一直追溯到很早以前,甚至跨越了复制点。它将创建分支作为历史记录中的一个事件来显示,因为

$ pwd
/home/sally/calc

$ svn log --verbose integer.c
------------------------------------------------------------------------
r344 | sally | 2002-11-07 15:27:56 -0600 (Thu, 07 Nov 2002) | 2 lines
Changed paths:
   M /calc/trunk/integer.c

* integer.c:  fix a bunch of spelling errors.

------------------------------------------------------------------------
r303 | sally | 2002-10-29 21:14:35 -0600 (Tue, 29 Oct 2002) | 2 lines
Changed paths:
   M /calc/trunk/integer.c

* integer.c:  changed a docstring.

------------------------------------------------------------------------
r98 | sally | 2002-02-22 15:35:29 -0600 (Fri, 22 Feb 2002) | 2 lines
Changed paths:
   M /calc/trunk/integer.c

* integer.c:  adding this file to the project.

------------------------------------------------------------------------

被隐式复制时,

/calc/trunk/

的所有内容都被复制了。现在,看看当莎莉在她的文件副本上运行相同的命令时会发生什么。

  1. 莎莉看到了她自己的修订版 344 更改,但没有看到您在修订版 343 中进行的更改。就 Subversion 而言,这两个提交影响了存储库不同位置的不同文件。但是,Subversion 确实 显示了这两个文件共享一个共同的历史记录。在修订版 341 中进行分支复制之前,它们曾经是同一个文件。这就是为什么您和莎莉都能够看到修订版 303 和 98 中进行的更改的原因。

  2. 分支背后的关键概念



从本节中,您应该记住两个重要的教训。