SVN的使用(四)

  SVN的使用 收藏
转载于:
http://www.svn8.com/svnsy/20080202/25.html

===========

                                                                        分支和合并


本文来自Svn中文网[www.svn8.com]转发请保留本站地址:http://www.svn8.com/svnsy/20080202/22.html

分支,标签和合并是所有版本控制系统的共有的概念。如果你不熟悉这些概念,我们在本章有一个很好的介绍。如果你熟悉,那么希望你会对看看Subversion如何来实现这些概念有兴趣。

Svn中文网


分支是版本控制的基本组成部分之一。如果你要让Subversion来管理你的数据,那么你将会非常依赖这个特性。本章假设你已经熟悉了Subversion的基本概念(第 2 章 基本概念)。

Bbs.Svn8.Com

什么是分支?假设你的工作是维护你的公司某个部门的文档——某种手册。某天,另一个不同的部门向你要相同的手册,但是需要为他们“调整”一小部分,因为他们的工作略有不同。 Svn中文网

这种情况下你该怎么做?显然你会这么做:你复制一份你的文档的副本,然后开始分别维护这两份文档。当其中一个部门要求你做一些修改时,你把它们合并到这个或那个副本。

Svn中文网


你常常要对两个副本作相同的修改。例如,如果你在一个副本中发现一个拼写错误,很可能另一副本中也有相同的错误。因为这两个文档绝大部分是相同的;他们只在很小的、特定的地方有差别。 参考资料:www.svn8.com

这就是分支的基本概念——就是说,一条与别的线独立的开发线,但是如果你往回看足够长时间的话,他们仍然共享一个共同的历史。一个分支总是从某些东西的复制品开始,从那里出发来产生它自己的历史(参见图 4.1 “开发的分支”)

Bbs.Svn8.Com


图 4.1. 开发的分支 Bbs.Svn8.Com

Subversion有帮助你维护你的文件和目录的并行分支的命令。它使你可以通过复制你的数据来创建分支,并记住这些副本是和另一个有联系的。它也帮助你把修改从一个分支复制到另一个。最后,它能使你工作副本的不同部分反映不同的分支,因此你可以在日常工作中“混合和搭配”不同的开发线。 Svn中文网


本文来自Svn中文网[www.svn8.com]转发请保留本站地址:http://www.svn8.com/svnsy/20080202/22.html

=================

                                                                            使用分支

到这里,你应该理解每次提交是如何在资料库中创建一个全新的文件系统树的(叫做一个“修订版”)。如果不理解,回头看看“修订版(Revision)”一节中关于修订版的部分。 Svn8.Com

在这一章,我们将回到和第二章相同的例子。记着你和你的合作者Sally在共享一个资料库,其中包括两个项目: paint和calc。然而注意在图 4.2 “开始时的资料库布局”中,每个项目目录现在包括名为trunk 和 branches的子目录。这么做的原因很快就会清楚了。

Www.Svn8.Com


图 4.2. 开始时的资料库布局 Bbs.Svn8.Com

和前面一样,假设Sally和你都有“calc”项目的工作副本。特别是,你们都有一个/calc/trunk 目录的工作副本。所有的项目文件都在这个子目录中而不是在/calc本身,因为你的团队决定把/calc/trunk目录作为开发进行的“主线”。 Svn8.Com

假设你得到了一个从根本上重新组织这个项目的任务。这将花费很长的时间来写,而且会影响项目中的所有文件。问题是你不想干扰Sally的工作,她正在修正各处一些小的Bug。她的工作需要项目中(在 /calc/trunk里)的最新的修订版总是稳定的。如果你一点一点的提交你的修改,你肯定会破坏Sally的工作。 参考资料:www.svn8.com

一种策略是闭门造车:你和Sally可以停止共享信息一两个星期。就是说,你可以开始剖析和重新组织你工作副本中所有的文件, 但不要提交或更新, 直到完成了全部任务。然而,这么做有很多问题。首先,这不太安全。大部分人喜欢经常地把他们的工作保存到资料库,以免在工作副本中有什么意外发生。第二,这不太灵活。如果你有多个用来工作的计算机上(可能你在两个不同机器上都有/calc/trunk的工作副本),你将不得不手工来回复制你的修改,或者只在一台计算机上做全部工作。同样的原因,和任何别的人共享你进行中的修改都很困难。软件开发中一个公认的“最佳实践”是:在你工作进行时让你的同伴来检查你的工作。如果没人能看到你的中间提交,你就失去了可能的反馈。最后,当你完成你所有的修改时,你可能发现很难把你的最后工作重新合并到公司的主体代码中。Sally(或其他人)可能在资料库中作了很多其他修改,这些修改很难合并到你的工作副本中——特别是如果你在几个星期的隔离后再运行 svn update。

参考资料:www.svn8.com


更好的解决办法是在资料库中创建自己的分支或开发线。这让你可以经常保存不完整的工作而不会干扰别人,而且你还能有选择的和你的合作者共享信息。接下来你会看到究竟是怎样做的。 参考资料:www.svn8.com

创建一个分支创建一个分支非常简单——你只要用svn copy命令在资料库中创建项目的一个拷贝。Subversion不但能复制单个文件,而且也能复制整个目录。在现在这个情景下,你要创建 /calc/trunk目录的一个拷贝;这个新拷贝应该放在哪儿?答案是任何你想放的地方——这是由项目的方针决定的。假设你的团队的方针是在资料库中的/calc/branches目录下创建分支,并且你决定把你的分支命名为my-calc-branch。你将需要创建一个新目录/calc/branches/my-calc-branch,它的生命开始于对/calc/trunk的复制。 参考资料:www.svn8.com

有两种不同的创建拷贝的办法。我们先演示麻烦的办法,只是为了使概念更清楚。首先,要检出项目根目录/calc的一个工作副本。 参考资料:www.svn8.com

$ 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.
 参考资料:www.svn8.com 现在创建拷贝就很简单了,只要传递两个工作副本路径给svn copy命令就行了:

Svn中文网

$ cd bigwc
$ svn copy trunk branches/my-calc-branch
$ svn status
A  +   branches/my-calc-branch
 Svn中文网 在这种情况下,svn copy命令递归的把工作目录trunk复制到一个新的工作目录。如你用 svn status命令看到的,新目录现在被预订要添加到资料库中。但也要注意到在字母A后面的 “+”符号。这表明预订要添加的是某些东西的拷贝,而不是新的。当你提交你的修改时, Subversion会在资料库中拷贝/calc/trunk来创建 /calc/branches/my-calc-branch,而不是通过网络重新传递所有的工作副本数据。

Svn8.Com


$ svn commit -m "Creating a private branch of /calc/trunk."
Adding         branches/my-calc-branch
Committed revision 341.
 Bbs.Svn8.Com 现在介绍比较容易的创建分支的方法,我们应该先告诉你这个:svn copy可以直接在两个URL上操作。 Bbs.Svn8.Com

$ 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.
 Svn中文网 这两种方法实际上没有什么区别。两个过程都在修订版341中创建了一个新的目录,这个新目录是 /calc/trunk 的一个拷贝。如图所示图 4.3 “有了新拷贝的资料库”。注意第二种办法, [6] 这个过程更简单,因为它不需要你检出资料库的庞大的镜像。事实上,这种技术甚至根本不需要你有工作副本。

Svn中文网

图 4.3. 有了新拷贝的资料库 Svn中文网

廉价复制 Www.Svn8.Com
Subversion资料库有一个特殊设计。当你复制一个目录时,你不用担心资料库会变得很大——Subversion没有实质上复制任何数据。而是创建了一个新的目录项来指向已经存在的树。如果你是一个Unix用户,这个和硬链接(hard-link)是相同的概念。因此,这种复制被认为是“懒惰的”。就是说,如果你提交复制的目录中某个文件的修改,那么只有这个文件改动了——其余的文件依然是对原始目录中原始文件的链接。

Www.Svn8.Com

这就是为什么你常常听到Subversion用户谈到“廉价复制”的原因。这和目录大小无关,对他的复制只花费很少的,固定长度的时间。事实上,这个特性是Subversion中提交操作的工作基础:每个修订版是前一个修订版的 “廉价复制”,再加上几个懒惰地修改了的项。(要了解更多,访问Subversion 网站,阅读在Subversion设计文档中关于“bubble up”方法的部分。) Svn中文网

当然,这些复制和共享数据的内部机制对用户是隐藏的,他们看到的只是树的复制。这儿的重点是复制在时间和空间上是廉价的。因此每当你想的时候就可以创建一个分支。 Www.Svn8.Com

在你的分支上工作现在你已经创建了项目的一个分支,你可以检出一个工作副本并开始使用它: Bbs.Svn8.Com

$ 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.
 
Www.Svn8.Com 这个工作副本没有任何特殊;它只是资料库中另一个目录的镜像。然而当你提交你的修改时,Sally在更新时并不会看到它们。她的工作副本是 /calc/trunk的拷贝。(请一定要读本章稍后的“切换工作副本”一节:svn switch是创建分支的工作副本的另一个方法。)

Bbs.Svn8.Com

让我们假设一个星期过去了,以下提交发生了: Www.Svn8.Com

你修改了 /calc/branches/my-calc-branch/button.c,这创建了修改版342。 Svn中文网

你修改了 /calc/branches/my-calc-branch/integer.c,这创建了修改版343。

Svn中文网


Sally修改了 /calc/trunk/integer.c,这创建了修改版343。 参考资料:www.svn8.com

现在有两个独立的开发线修改了integer.c,如图所示图 4.4 “一个文件历史的分支”: 参考资料:www.svn8.com

图 4.4. 一个文件历史的分支
Www.Svn8.Com


当你查看你的integer.c的副本的历史时,会发现很有趣的事情:

Www.Svn8.Com


$ 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.

------------------------------------------------------------------------
 Svn中文网 注意Subversion顺着时间追溯你的分支中的integer.c的所有历史,甚至穿过了它被复制的那一点。分支的创建作为历史中的一个事件显示,因为integer.c是随着所有包含在/calc/trunk/ 中的项的复制而被复制的。现在看看当Sally对她的文件副本执行相同的命令时是什么情况:

Svn8.Com

$ 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.

------------------------------------------------------------------------
 Svn中文网 Sally看到了她自己的修订版344的修改,但看不到你在修订版343做的修改。Subversion认为,这两个提交影响了不同位置的不同的两个文件。但是,Subversion显示了这两个文件共享一个共同的历史。在修订版341分支创建前,它们是同一个文件。这既是为什么你和Sally都可以看到在修订版303和98做的修改。
Www.Svn8.Com

分支背后的关键思想本节你应该记住两个重要的结论: Bbs.Svn8.Com

Subversion和其他版本控制系统不同,它的分支在资料库中就是一个普通的文件系统目录,不是特殊的东西。只是这些目录带了一些额外的历史信息。 参考资料:www.svn8.com

Subversion内部没有分支的概念,只有复制。当你复制一个目录时,得到的目录是一个“分支”,只是因为你赋予它这样的意义。你可以认为这个目录特别,或者特别的对待它,但对于Subversion来说那只是普通的目录,只不过碰巧是通过复制来创建的而已。

Bbs.Svn8.Com

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

[6] Subversion不支持资料库之间的复制。当在svn copy 或 svn move中使用URL时,你只能复制在同一资料库中的项。 Svn8.Com

本文来自Svn中文网[www.svn8.com]转发请保留本站地址:http://www.svn8.com/svnsy/20080202/23.html

============

                                                                         在分支之间复制修改

现在你和Sally分别工作在项目的两个并行的分支上:你在一个私有的分支上工作,Sally在trunk,或者说开发主线上工作。

Www.Svn8.Com


由于项目有很多的开发者,通常大部分人有主线的一个工作副本。当某人要做需时较长的修改而可能会破坏主线时,标准的过程是创建一个私有的分支来提交修改,直到工作全部完成。

参考资料:www.svn8.com


这样做,好消息是你和Sally不会互相干扰,坏消息是很容易偏移的太远。要记得“闭门造车”策略的问题是,在你完成你分支后,要把你的修改合并到主干里而没有大量的冲突,几乎是不可能的。

Www.Svn8.Com


更好的办法是,在工作时,你和Sally可以继续共享修改。决定哪些修改需要共享是你们的职责。 Subversion赋予你有选择的在分支之间“复制”修改的能力。而且当你全部完成你的工作后,你能把分支中的全部修改复制回主干去。

Www.Svn8.Com

复制指定的修改在前面的章节中,我们提到你和Sally都在各自的分支中对integer.c作了修改。如果你察看在修改版344写的日志消息,你可以看到她修正了一些拼写错误。无疑,你的同一文件的拷贝仍然有同样的拼写错误。可能你将来的修改影响这些有拼写错误的地方,因此可能会在以后某天合并你的分支时导致冲突。那么,更好的办法是,在你在相同的地方做太多的工作之前,现在就接收Sally的修改,。

Bbs.Svn8.Com

现在是使用svn merge命令的时候了。这个命令是svn diff命令(你在第三章读到过)的很近的表兄弟。这两个命令都可用来比较资料库中任何两个对象。例如,你可以让svn diff来显示Sally在修改版344做的确切的修改。

Bbs.Svn8.Com


$ svn diff -r 343:344 http://svn.example.com/repos/calc/trunk

Index: integer.c
===================================================================
--- integer.c (revision 343)
+++ integer.c (revision 344)
@@ -147,7 +147,7 @@
     case 6:  sprintf(info->operating_system, "HPFS (OS/2 or NT)"); break;
     case 7:  sprintf(info->operating_system, "Macintosh"); break;
     case 8:  sprintf(info->operating_system, "Z-System"); break;
-    case 9:  sprintf(info->operating_system, "CPM"); break;
+    case 9:  sprintf(info->operating_system, "CP/M"); break;
     case 10:  sprintf(info->operating_system, "TOPS-20"); break;
     case 11:  sprintf(info->operating_system, "NTFS (Windows NT)"); break;
     case 12:  sprintf(info->operating_system, "QDOS"); break;
@@ -164,7 +164,7 @@
     low = (unsigned short) read_byte(gzfile);  /* read LSB */
     high = (unsigned short) read_byte(gzfile); /* read MSB */
     high = high << 8;  /* interpret MSB correctly */
-    total = low + high; /* add them togethe for correct total */
+    total = low + high; /* add them together for correct total */
 
     info->extra_header = (unsigned char *) my_malloc(total);
     fread(info->extra_header, total, 1, gzfile);
@@ -241,7 +241,7 @@
      Store the offset with ftell() ! */
 
   if ((info->data_offset = ftell(gzfile))== -1) {
-    printf("error: ftell() retturned -1./n");
+    printf("error: ftell() returned -1./n");
     exit(1);
   }
 
@@ -249,7 +249,7 @@
   printf("I believe start of compressed data is %u/n", info->data_offset);
   #endif
  
-  /* Set postion eight bytes from the end of the file. */
+  /* Set position eight bytes from the end of the file. */
 
   if (fseek(gzfile, -8, SEEK_END)) {
     printf("error: fseek() returned non-zero/n");
 Svn中文网 svn merge几乎是一样的。然而它不是把差别打印到你的终端上,而是把差别像本地修改那样直接应用到你的工作副本上。

Www.Svn8.Com

$ svn merge -r 343:344 http://svn.example.com/repos/calc/trunk
U  integer.c

$ svn status
M  integer.c
 Bbs.Svn8.Com svn merge的输出显示了你的integer.c的副本被修补了。现在它包含了Sally的修改——修改已经从主干“复制”到你的私有分支了,现在就如你的本地修改一样。这时,你需要检查本地的修改来确认它是正确的。 Bbs.Svn8.Com

在另一个场景中,可能事情没那么顺利,integer.c会进入到冲突状态。你可能需要用标准的过程(参见第三章)来解决冲突,或者如果你认定合并完全是个坏主意,那么可以简单的放弃它,并用svn revert恢复本地的修改。

Bbs.Svn8.Com

假设你已经检查完了合并后的修改,你可以像平常一样用svn commit提交修改。这时,修改已经被合并到你的资料库分支里。在版本控制术语中,在分支之间复制修改通常被称为移植(porting) 修改。 参考资料:www.svn8.com

当你提交本地修改时,应该确认你的日志消息记录了你从一个分支移植了某个特定的修改到另一个分支,例如: 参考资料:www.svn8.com

$ svn commit -m "integer.c: ported r344 (spelling fixes) from trunk."
Sending        integer.c
Transmitting file data .
Committed revision 360.
 Svn中文网 如你在下一节将看到的,这是一个应该遵循的、重要的“最佳实践”。

Bbs.Svn8.Com

为什么不用补丁来代替 参考资料:www.svn8.com
一个问题可能出现在你脑海中,特别是如果你是一个Unix用户:为什么要用svn merge那么麻烦?为什么不用操作系统的patch命令来完成同样的工作呢?例如:

Svn中文网


$ svn diff -r 343:344 http://svn.example.com/repos/calc/trunk > patchfile
$ patch -p0  < patchfile
Patching file integer.c using Plan A...
Hunk #1 succeeded at 147.
Hunk #2 succeeded at 164.
Hunk #3 succeeded at 241.
Hunk #4 succeeded at 249.
done
 
Www.Svn8.Com 是的,在特定的情况下,这确实没什么不同。但svn merge有超过patch程序的特殊能力。 patch可用的文件格式非常有限,它只能调整文件内容。它没有办法表现文件树的修改,比如文件和目录的添加,删除或重命名。如果Sally的修改中有这样的操作,比如添加了一个新目录,svn diff 的输出根本不会提到这些。svn diff仅仅输出有局限性的补丁格式,因此它完全不能表达这些。 [7] 然而,svn merge命令能直接把树修改应用到你的工作副本上。

Svn8.Com

一些警告:虽然svn diff和svn merge在概念上很相似,但是很多情况下它们有不同的语法。一定要阅读第九章以得到更多的细节,或者使用svn help。例如,svn merge需要一个工作副本路径作为目标,就是说,要有一个它能应用树修改的地方。如果没有指定目标,它假设你在尝试执行以下某个常用操作之一: Www.Svn8.Com

你想把对目录的修改合并到你现在的工作副本中。 Www.Svn8.Com

你想把对一个特定文件的修改合并到你当前工作目录的同名文件中。

Bbs.Svn8.Com

如果你在合并一个目录而且没有指定目标路径,svn merge假设是以上的第一种情况,并且假设把修改应用到你的当前目录。如果你在合并一个文件,而且这个文件(或者同名的文件)存在于你的当前工作目录中, svn merge假设是以上的第二种情况并尝试把修改应用到本地的同名文件上。

Svn8.Com


如果你想把修改应用到别的地方,你需要指出来。例如,如果你工作于你工作副本的父目录里,你要指定接收修改的目标目录。

Www.Svn8.Com

$ svn merge -r 343:344 http://svn.example.com/repos/calc/trunk my-calc-branch
U   my-calc-branch/integer.c
 Bbs.Svn8.Com 合并的最佳实践手工跟踪合并合并修改似乎很简单,但在实践中可能会出很令人头疼。问题在于,如果你不断地把修改从一个分支合并到另一个,你可能不小心把同一个修改合并了两次。如果发生了这种事,有时事情进行良好。当修补一个文件时,Subversion通常会注意到文件已经被修改了,而不会做任何事。但如果已存在的修改又已经被改动了,你会得到一个冲突。
Www.Svn8.Com

理想情况下,你的版本控制系统应该防止把修改在一个分支上应用两次。它应该自动记住一个分支已经接收到了哪些修改,并且能给你把它们列出来。它应该用这些信息尽可能使合并自动化。

Bbs.Svn8.Com


不幸的是,Subversion不是这种系统。就像CVS,Subversion 没有记录任何关于合并操作的信息。当你提交本地修改时,资料库不知道那些修改来自于执行svn merge的结果还是只是对文件的手工修改。

Svn8.Com


这对你,一个用户,有什么意义?这意味着除非某天Subversion有了这个特性,否则你将不得不自己跟踪合并信息。这么做的最好的地方就是提交时的日志消息。像在前面的例子中演示的,我们建议在你的日志消息中提及那个合并到你的分支中的特定的修改版号(或修订版范围)。以后你能用svn log来检查你的分支已经包含了哪些修改。这使你能仔细地构建一系列svn merge命令而不会和先前移植的修改重复。

Www.Svn8.Com


在下一节,我们将展示一些应用这个技术的例子。 Svn8.Com

预览合并因为合并只影响本地修改,所以通常不是高风险的操作。如果你第一次合并错了,简单的用svn revert 撤销修改后再试一次。

Bbs.Svn8.Com


然而,也可能你的工作副本已经有了本地修改。合并带来的修改和你先前的修改混合了,执行svn revert 不再可行。这两套修改不可能分离开。

Www.Svn8.Com


在这种情况下,能在发生前预知或检查合并会让人们感到放心。一个简单的办法是执行svn diff,并带着和你计划传递给svn merge的相同的参数,像我们已经在合并的第一个例子里展示的那样。另一种预览的办法是传递--dry-run选项给合并命令: Svn8.Com

$ svn merge --dry-run -r 343:344 http://svn.example.com/repos/calc/trunk
U  integer.c

$ svn status
#  nothing printed, working copy is still unchanged.
 
Www.Svn8.Com --dry-run选项不会实际应用任何本地修改到工作副本。它只是显示真实合并会 打印出的状态码。如果觉得执行svn diff给出的详细信息太多时,得到对可能的合并的“高层次”的预览是有用的。

Svn8.Com


Subversion和变更集 Svn中文网
好像每个人的“变更集”的概念都有些不同,或者至少是对版本控制系统的“变更集特性”有不同的期望。对我们来说,变更集只是一个修改的集合并有唯一的名字。这些修改可能包括对一个文件内容的文本编辑,对树结构的修改,或对元数据的调整。更通俗的说,变更集只不过是一个有你能查阅的名字的补丁(patch)。

Www.Svn8.Com

在Subversion里,一个全局的修订版号码N命名了资料库中的一个文件树:它是在N次提交后资料库看起来的样子。它也是一个未言明的变更集的名字:如果你比较文件树N和N-1,你能推断出提交的确切的补丁。由于这个原因,很容易把“修订版N”看作一个变更集而不仅是一个树。如果你使用一个问题跟踪器来管理bug,你可以使用修订版号码来指向修正bug的特定的补丁——例如,“这个问题在修订版9238被修正了”。人们可以用 svn log -r9238命令来察看修正了这个bug的确切的变更集,并使用svn diff -r9237:9238 来看补丁本身。Subversion的合并命令也使用修订版号码。通过在合并参数里指定名字,你可以从一个分支把特定的变更集合并到另一个: svn merge -r9237:9238将把变更集#9238合并进你的工作副本。

参考资料:www.svn8.com

合并冲突就像svn update命令,svn merge把修改应用到你的工作副本。因此它也能产生冲突。但是,svn merge所产生的冲突有时会不太一样,本节将解释这些不同。

Www.Svn8.Com

开始,假设你的工作副本没有本地修改。当你用svn update更新到特定的修订版时,服务器发来的修改被“干净的”应用到你的工作副本。服务器通过比较两个树来产生增量:你工作副本的虚拟快照和你感兴趣的修订版树。因为用来比较的左手边和你已经有的完全相同,增量可以保证正确的把你的工作副本转换到右手边的树。 Bbs.Svn8.Com

但是svn merge没有这样的保证,可能会混乱很多:用户可以让服务器比较任何两个树,甚至那些和工作副本不相干的修订版!这意味着可能发生很多人为错误。用户可能有时比较错了两个树, 产生了一个不能干净的应用的增量。 svn merge会尽最大努力来应用尽可能多的增量,但某些部分也许不可能应用。就像Unix中的patch 有时会报怨“失败的块”,svn merge会抱怨“跳过了某些目标”:。

Svn中文网

$ svn merge -r 1288:1351 http://svn.example.com/repos/branch
U  foo.c
U  bar.c
Skipped missing target: 'baz.c'
U  glub.c
C  glorb.h

$
 参考资料:www.svn8.com 在前面的例子中,这种情况会出现:当baz.c同时存在于两个被比较的分支快照中,得到的增量要改变这个文件的内容,但是这个文件在工作副本中不存在。不管在什么情况下,“跳过”消息意味着用户很可能比较了两个错误的文件树;它是人为错误的典型标记。当这发生时,可以很容易的递归的撤销合并(svn revert --recursive)所做的所有的修改,在恢复后,删除所有留下的没有版本化的文件和目录,再用不同的参数重新执行svn merge。

Svn中文网


也要注意,前面的例子显示了在glorb.h上发生了一个冲突。我们是从没有本地修改的工作副本开始的:怎么会发生冲突呢? 这也是因为用户可以用svn merge来定义和应用任何旧的增量到工作副本,那个增量可能包含那些没有干净的应用到工作文件的文本修改,即时这个文件没有本地修改。 Bbs.Svn8.Com

svn update和svn merge之间的另一小差别是当冲突发生时产生的纯文本文件的名字。在“解决冲突(合并别人的修改)”一节,我们看到更新产生的文件名是filename.mine, filename.rOLDREV和 filename.rNEWREV。当svn merge产生冲突时,它产生的三个文件名为filename.working, filename.left和 filename.right。这种情况下,名词“left”和 “right”描述了这个文件来自被比较的两个树的那一边。在任何情况下,这些不同的名字可以帮助你分辨冲突是更新的结果还是合并导致的。 参考资料:www.svn8.com

注意或忽略血统当你和一个Subversion开发者交谈时,你很可能会听到他说术语血统。这个词用来描述资料库中两个对象之间的联系:如果它们互相联系,那么其中一个被称为另一个的祖先。

Svn中文网


例如,假设你提交了修订版100,它包括对foo.c的一个修改。那么foo.c@99foo.c@100的一个祖先。从另一方面说,假设你在修订版101提交了对foo.c的删除,并在修订版102添加了一个同名的文件。这种情况下,foo.c@99foo.c@102看起来好像有联系(它们有相同的路径),但事实上它们在资料库中是完全不同的对象。它们没有共同的历史或“血统”。

Svn中文网


指出这一点是为了说明svn diff和svn merge的一个重要的不同。前一个命令忽略血统,而后一个对血统很敏感。例如,如果你用svn diff来比较修订版99和102中的foo.c,你会看到基于行的diff输出;diff 命令只是盲目的比较这两个路径指定的文件。但如果你用svn merge来比较同样这两个对象,它会注意到它们互不相干,并先尝试删除旧文件然后添加新文件。你将会看到D foo.c 跟着一个A foo.c。 Www.Svn8.Com

大部分合并涉及到比较有血统关系的两个树,因而svn merge。但偶尔你也可能想用合并命令比较两个无关的树。例如,你可能已经导入了两个源代码树,它们分别代表同一个软件项目的不同的发行版本(参见???)。如果你用 svn merge 来比较这两个树,你将看到的是第一个树被整个删掉,跟着添加了整个第二个树。

Svn8.Com

在这些情形下,你会想用svn merge来只做基于路径的比较,而忽略文件和目录间的任何联系。你可以在合并命令中加上--ignore-ancestry选项,它将像svn diff那样运转。(对应的,--notice-ancestry选项会导致svn diff 像merge那样运行)。

Www.Svn8.Com


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

[7] Subversion项目计划在将来使用(或发明)一个扩展的补丁格式来描述树的修改。

本文来自Svn中文网[www.svn8.com]转发请保留本站地址:http://www.svn8.com/svnsy/20080202/24.html

================

 

常见的用例分支和svn merge命令有很多不同的用途,本节描述那些你可能碰到的最常用的用法。 参考资料:www.svn8.com

把整个分支合并到另一个为了完成我们的例子,我们要向前一点时间。假设几天已经过去了,在主线和你的私有分支上都有了很多修改。假设有已经完成了在你私有分支上的工作,新特性或者bug修正终于完成了,现在你想把你的分支中所有的修改都合并回主线以便别人欣赏。 Bbs.Svn8.Com

那么在这个场景下我们应怎样使用svn merge?要记得这个命令比较两个树并把差异应用到工作副本。因此要接收修改,你应该有主线的工作副本。我们假设或者你已经有一个在那儿(完全更新了),或者你刚刚检出了一个 /calc/trunk的新的工作副本。

Www.Svn8.Com

但是应该比较那两个树呢?初看起来,答案似乎很明显:就比较最新的主线树和你最新的分支树。但是小心—— 这个假设是错误的,已经欺骗了很多新用户!既然 svn merge 操作起来像svn diff,比较最新的主线和分支树将不仅仅描述你对你的分支作的那些修改。这样的比较会显示太多的修改:它将不仅显示添加了你的修改,也会显示除去了主线中那些没有发生在你的分支的修改。

参考资料:www.svn8.com

为了只表达发生在你的分支中的修改,你需要比较你的分支的初始状态和最终状态。在你的分支上使用svn log,你可以看到你的分支是在修订版341创建的。你的分支最终的状态可以简单的用修订版HEAD表示。这意味着你要比较修订版341和你的分支目录的HEAD,并把这些差别应用到主线的工作副本。 参考资料:www.svn8.com

提示发现分支是在哪个修订版创建(分支的“base”)的好办法是使用带--stop-on-copy选项的svn log。log子命令通常将显示分支上的每次修改,包括回溯到创建分支的那次复制。因此通常你也会看到来自主线的历史。--stop-on-copy选项将使svn log在一探测到目标被复制或重命名时就停止输出。 Www.Svn8.Com

因此,在我们继续进行的例子里,

Bbs.Svn8.Com


$ svn log --verbose --stop-on-copy /
         
http://svn.example.com/repos/calc/branches/my-calc-branch

------------------------------------------------------------------------
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)

$
 Svn中文网 如我们所期望的,这个命令打印出的最后的修订版是my-calc-branch通过复制创建的修订版。 Svn8.Com

这里是最后的合并过程: Www.Svn8.Com

$ cd calc/trunk
$ svn update
At revision 405.

$ svn merge -r 341:HEAD http://svn.example.com/repos/calc/branches/my-calc-branch
U   integer.c
U   button.c
U   Makefile

$ svn status
M   integer.c
M   button.c
M   Makefile

# ...examine the diffs, compile, test, etc...

$ svn commit -m "Merged my-calc-branch changes r341:405 into the trunk."
Sending        integer.c
Sending        button.c
Sending        Makefile
Transmitting file data ...
Committed revision 406.
 Svn中文网 此外要注意提交日志消息中特别提到被合并到主线中的修改的范围。始终记着要这么做,因为这是你将来会用的到的关键信息。

参考资料:www.svn8.com


例如,设想你决定继续在你的分支上工作一个星期,来完成你的新特性的改进或修正bug。现在资料库的HEAD修订版是480,你完成了,并要把你的私有分支再次合并到主线。但是如“合并的最佳实践”一节中讨论的,你不想再次合并你以前已经合并过的修改;你只想合并你的分支中所有上次合并以来的“新的”修改。这里有个技巧可以找出那些是新的。

参考资料:www.svn8.com

第一步是在主线上运行svn log,来寻找关于你上次从分支中合并的日志消息: Svn中文网

$ cd calc/trunk
$ svn log

------------------------------------------------------------------------
r406 | user | 2004-02-08 11:17:26 -0600 (Sun, 08 Feb 2004) | 1 line

Merged my-calc-branch changes r341:405 into the trunk.
------------------------------------------------------------------------

 
Www.Svn8.Com 啊哈!既然所有在修订版341和405之间发生在分支上的修改先前已经被合并到主线并成为了修订版406,现在你知道你只要合并那之后的分支修改 ——通过比较修订版406和HEAD。

参考资料:www.svn8.com

$ cd calc/trunk
$ svn update
At revision 480.

# We notice that HEAD is currently 480, so we use it to do the merge:

$ svn merge -r 406:480 http://svn.example.com/repos/calc/branches/my-calc-branch
U   integer.c
U   button.c
U   Makefile

$ svn commit -m "Merged my-calc-branch changes r406:480 into the trunk."
Sending        integer.c
Sending        button.c
Sending        Makefile
Transmitting file data ...
Committed revision 481.
 Bbs.Svn8.Com 现在主线包含了分支里第二波的全部修改。这时,你可以删除你的分支(稍后我们会讨论),或继续在分支上工作并重复后续的合并。

参考资料:www.svn8.com

撤销修改svn merge另一个常见用途是回滚一个已经提交的修改。假设你在/calc/trunk一个工作副本上愉快的工作着,你发现在以前在修订版303对integer.c做了修改是完全错误的。它不该被提交。你可以用svn merge命令来“撤销”你工作副本中的修改,然后再把本地改动提交。你要作的就是指定一个逆向的差异:

参考资料:www.svn8.com


$ svn merge -r 303:302 http://svn.example.com/repos/calc/trunk
U  integer.c

$ svn status
M  integer.c

$ svn diff

# verify that the change is removed

$ svn commit -m "Undoing change committed in r303."
Sending        integer.c
Transmitting file data .
Committed revision 350.
 参考资料:www.svn8.com 一种看待资料库修订版的方法是把它看作一组特定的修改(有些版本控制系统称这变更集)。通过使用-r选项,你能用svn merge来应用一个变更集、或者是一定范围内的变更集到你的工作副本。在我们这个撤销修改的情况下,我们用svn merge来把变更集#303应用到我们的工作副本。

Svn8.Com

切记像这样回滚一个修改和其它svn merge操作一样,因此你应该使用svn status和svn diff命令来确认你的工作在你想要的状态,然后使用svn commit把最后的版本发到资料库。在提交后,这个特定的变更集将不再反映在HEAD修订版。 参考资料:www.svn8.com

此外,你可能会想:哦,实际上提交没有撤销,是吧?修改仍然存在于修订版303。如果某人检出calc 项目在修订版303和349之间的某个版本,他们仍然将看到哪个错误的修改,对吧?

Svn中文网

是的,这是对的。当我们谈到“除去”一个修改,我们实际上在说从HEAD中除去它。原来的修改仍然存在于资料库的历史中。在大部分情况下,这足够好了。毕竟大部分人只对跟踪HEAD有兴趣。但也有些特殊情况,你确实想消除所有提交的痕迹(可能某人不小心提交了一个保密文档)。事实表明这不那么容易,因为 Subversion特意设计成从不丢失信息。修订版是互相依赖的不可修改的树。除去一个修订版会导致多米诺效应,对所有后续的修订版造成混乱,还可能使所有的工作副本无效。 [8]

Svn8.Com


恢复删掉的项版本控制系统的一个重大的特性是信息永不会丢失。甚至在你删除了一个文件或目录时,它可能从HEAD 中去掉了,但是这个对象仍然存在于以前的修订版里。一个新用户最常问的问题之一是:“我怎么才能把我的旧文件或目录弄回来?” Bbs.Svn8.Com

第一步是定义究竟哪个是你要恢复的项。这有个有用的隐喻:你可以把资料库中的每一个对象想象为存在于一种两维坐标系统中。第一个坐标是一个特定的修订版树,第二个坐标是这个树中的一个路径。因此你文件或目录的每个修订版都可以用一个特定的坐标点来定义。

参考资料:www.svn8.com


Subversion 没有像CVS那样的Attic目录。 [9] 因此你需要使用svn log 来发现你想恢复的项的精确坐标。一个好策略是在曾经包含你删掉的项的目录中运行svn log --verbose。--verbose选项显示每个修订版中所有修改了的项的列表。所有你需要做的是找到你删除文件或目录的哪个修订版。你可以直接寻找,或者使用别的工具来检查日志输出(用grep,或者在编辑器中用增量搜索)。

Svn中文网


$ cd parent-dir
$ svn log --verbose

------------------------------------------------------------------------
r808 | joe | 2003-12-26 14:29:40 -0600 (Fri, 26 Dec 2003) | 3 lines
Changed paths:
   D /calc/trunk/real.c
   M /calc/trunk/integer.c

Added fast fourier transform functions to integer.c.
Removed real.c because code now in double.c.

 Svn8.Com 在这个例子里,我们假定你在寻找一个删除了的real.c文件。通过检查父目录的日志,你已经发现这个文件是在修订版808被删除的。而且,文件存在的最后一个修订版就是这个修订版之前的那个。结论:你想从修订版807恢复/calc/trunk/real.c。

Www.Svn8.Com


以上是困难的部分——查找。现在你知道你要恢复什么了,你有两个不同的选择。

Bbs.Svn8.Com


一个选项是用svn merge来“逆向”应用修订版808(我们已经讨论过怎样撤销修改,参见“撤销修改”一节)。这将导致把real.c作为一个本地修改重新添加。这文件将被预订添加,在提交后,这个文件会再次出现在HEAD中。 Bbs.Svn8.Com

但是,在这个特殊的例子里,这可能不是最好的策略。逆向应用修订版808将不仅预订添加real.c,日志消息也显示了这也会撤销对文件integer.c的某些修改,而这是你不想的。当然,你可以逆向合并修订版808然后svn revert对integer.c的本地修改,但这种技术不好大规模应用。如果在修订版808有90个文件修改了会怎样? Svn中文网

另一种更目标明确的策略是根本不要用svn merge,而是用svn copy命令。仅仅从资料库把准确的修订版和路径指定的“坐标点”复制到你的工作副本。 参考资料:www.svn8.com

$ svn copy --revision 807 /
          
http://svn.example.com/repos/calc/trunk/real.c ./real.c

$ svn status
A  +   real.c

$ svn commit -m "Resurrected real.c from revision 807, /calc/trunk/real.c."
Adding         real.c
Transmitting file data .
Committed revision 1390.
 Svn8.Com 状态输出中的加号表示这一项不仅预订要添加,而且预订“带着历史”添加。Subversion记录它是从哪儿复制来的。将来,在这个文件上执行svn log会回溯到文件的恢复并一直到它在修订版807之前的所有历史。换句话说,新文件real.c 不是真的新,它是原来的被删除了的文件的后代。
Www.Svn8.Com

虽然我们的例子是恢复一个文件,但是要说明的是同样的技术也可以用来恢复删除了的目录。

Bbs.Svn8.Com


常见的分支模式版本控制最常用于软件开发,因此这里简单介绍两个在程序员团队中最常用的分支/合并模式。如果你不是用Subversion 、来做软件开发,跳过这节也没关系。如果你是头一回使用版本控制的软件开发者,要认真阅读,因为这些模式通常被有经验的人们认为是最佳实践。这些过程不是特定于Subversion的;它们对任何版本控制系统都有效。同样,用Subversion的术语来描述有助于理解它们。

参考资料:www.svn8.com

发行版分支大部分软件有一个典型的生命周期:编码,测试,发现,然后重复这个过程。这个过程有两个问题。首先,在质量保证团队在测试假定为稳定的软件版本时,开发人员需要继续写新的特性。在软件被测试时,新的开发不能停止。第二,团队几乎总是需要支持软件的旧的,发行了的版本;如果在最新的代码里发现了一个bug,它很可能也存在于发行了的版本中,客户会想得到修正而不用等新的主版本发布。 Svn8.Com

这是版本控制能帮忙的地方。典型的过程像这样:

Svn8.Com


开发人员把所有的新工作提交到主线。 日常的修改被提交到/trunk:新特性,bug修正等等。 Bbs.Svn8.Com

主线被复制到“发行版”分支。 当团队认为软件可以发布了(比如,1.0版),那么/trunk可以被复制到/branches/1.0。 Www.Svn8.Com

团队继续并行的工作着。 一个团队开始严格测试发行版分支,同时另一个团队在/trunk上继续新的工作(比如,开发2.0版)。如果某个位置发现了bug,必要时修正可以在分支之间来回移植。但有时,甚至这样的过程也要停下来。分支被“冻结”以在一次发布前作最后的测试。 Svn8.Com

分支被加标签并发布 当测试完成时,/branches/1.0被复制到/tags/1.0.0作为一个供参考的快照。这个tag被打包并发布给客户。 Www.Svn8.Com

分支被维护着 当/trunk上2.0版的工作进行时,bug修正继续从/trunk移植到 /branches/1.0。当积累了足够多的bug修正后,管理层可能决定做一个1.0.1发行版: /branches/1.0被复制到/tags/1.0.1,然后tag被打包并发布。

Svn8.Com


随着软件的成熟,整个过程不断重复:当2.0的工作完成,一个新的2.0发行版分支被创建,测试,加标签,并最终发布。几年后,资料库结束于这样的状态:有很多发行版分支处于“维护”模式,和很多代表最终发布的版本。

Bbs.Svn8.Com


特性分支特性分支(feature branch)就是在本章中作为主要例子的那种分支,那个当Sally在/trunk工作时你已经在工作的分支。它是一个临时的分支,在做一个复杂的改动时创建以免影响/trunk的稳定。不像发行版分支(它可能需要你一直支持),特性分支出生,使用一段时间,合并回主线,然后彻底删除。它们在有限的时段有用。 Svn中文网

此外,在究竟什么时候适合创建一个分支上,项目方针有很大的不同。有些项目从来不用分支:提交到/trunk 是完全自由的。这种系统的优点是它简单——没人需要学习分支和合并。缺点是主线代码常常是不稳定或不可用的。另外一些项目把分支用到了极至:任何修改都不能直接提交到主线。甚至最小的修改也要创建一个短命的分支,仔细地检查并合并到主线。然后删除分支。这种系统保证所有时间都有一个非常稳定和可用的主线,但代价是经常性的巨大的过程开支。 Www.Svn8.Com

大部分项目采取了一种中庸之道。它们通常坚持任何时候/trunk能编译和通过回归测试。特性分支仅在一个修改需要大量不稳定的提交时创建。一个好的判断规则是问这样一个问题:如果开发者单独工作一些天然后一次提交全部的修改(以使 /trunk永远不会不稳定),这修改会太大以致无法检查吗?如果对这个问题的回答是“是”,那么这个修改应该在一个特性分支里开发。当这个开发者向这个分支提交不断增加的修改时,同伴可以很容易的检查。

参考资料:www.svn8.com

最后还有一个问题是在工作进行时如何保证一个特性分支和主线最好的同步。如我们之前提到的,在一个分支上工作几个星期或几个月有一个很大的风险;随着主线修改不断大量注入,最后到了这样的地步,两条开发线的差别如此之大,以至于要把分支合并回主线成为一场噩梦。

Svn中文网

最好规律性的把主线的修改合并到分支来避免这种情况。制定一个政策:每星期一次,把主线上星期的修改合并到分支里。在这么做的时候要注意;合并要手工来跟踪以避免重复合并的问题(如同在“手工跟踪合并”一节描述的)。你需要仔细地写日志消息,详细到究竟哪个修订版范围已经被合并了(如同在“把整个分支合并到另一个”一节演示的)。这听起来挺难的,但实际上很容易做到。 Svn中文网

在某个时候,你已经准备好把“同步的”特性分支合并回主线。这么做,首先要做最后的一次合并,把最后的主线修改合并到分支。完成后,最后的分支版本和主线除了你的分支中的修改外将完全相同。因此,在这种特殊情况下,你将通过比较分支和主线来合并:

Svn中文网


$ cd trunk-working-copy

$ svn update
At revision 1910.

$ svn merge http://svn.example.com/repos/calc/trunk@1910 /
           
http://svn.example.com/repos/calc/branches/mybranch@1910 /
U  real.c
U  integer.c
A  newdirectory
A  newdirectory/newfile

 Svn8.Com 通过比较主线的HEAD修订版和分支的HEAD修订版,你定义了一个仅仅描述了你在分支做的修改的增量;因为两个开发线都已经有了所有主线中的修改。

参考资料:www.svn8.com


另一个看待这个模式的方式是把你每周和主线的同步和在工作副本中运行svn update类比,并把最后的合并步骤和从一个工作副本运行svn commit类比。毕竟,一个工作副本和一个非常短命的私有分支有什么区别呢?它是一个仅仅能在某时保存一个修改的分支。 Svn8.Com

 

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

[8] 不过,Subversion项目已经计划在将来实现一个svnadmin obliterate,来完成永久删除信息的任务。同时,可参考“svndumpfilter”一节寻找可能的办法。

参考资料:www.svn8.com


[9] 因为CVS不版本化树,它在每个资料库目录中创建Attic作为记住删除文件的办法 Svn8.Com

本文来自Svn中文网[www.svn8.com]转发请保留本站地址:http://www.svn8.com/svnsy/20080202/25.html

==============

                                                                  切换工作副本
本文来自Svn中文网[www.svn8.com]转发请保留本站地址:
http://www.svn8.com/svnsy/20080202/26.html

svn switch命令把现有的工作副本转换为一个别的分支。这个命令不严格局限用于分支,它给用户提供了一个很好的快捷方式。在我们前面的例子里,在创建你的私有分支后,你检出了这个资料库中新目录的一个新的工作副本。作为替代方法,你可以简单的让Subversion来把/calc/trunk的工作副本转换为新分支位置的镜像: Bbs.Svn8.Com

$ cd calc

$ svn info | grep URL
URL:
http://svn.example.com/repos/calc/trunk

$ svn switch http://svn.example.com/repos/calc/branches/my-calc-branch
U   integer.c
U   button.c
U   Makefile
Updated to revision 341.

$ svn info | grep URL
URL:
http://svn.example.com/repos/calc/branches/my-calc-branch
 Svn中文网 在“切换”到另一个分支后,你的工作副本和你做对目录的一次新检出得到的没什么不同。而且通常用这个命令更有效率,因为通常各个分支仅仅有很小的不同。服务器仅仅发送很小的必要的修改来使你的工作副本体现为那个分支目录。

Www.Svn8.Com

svn switch命令也可以带有--revision(-r)选项,因此你不需要总是把你的工作副本移到分支的“顶端”。 Svn8.Com

当然,大部分项目比我们的calc例子要复杂的多,包含多个子目录。Subversion用户在使用分支时通常遵循一个特定的算法。

Svn8.Com


复制项目的整个“主线”到一个新的分支目录。

参考资料:www.svn8.com


仅切换主线的一部分来反映分支。 Bbs.Svn8.Com

换句话说,如果你个用户知道那个分支的工作仅需要在一个特定的子目录里发生,他们使用svn switch 来仅把这个子目录转换到那个分支。(有时候,用户仅仅切换单独的一个文件到分支!)这种方式,他们能继续在他们工作副本的绝大部分上接受日常的“主线”更新,但切换了的部分将仍然不受影响(如果没有人再他们的分支上提交修改的话)。这个特性给“混合工作副本”这个概念增加了全新的维度——工作副本不仅能包含修订版的混合,而且也能包含不同资料库位置的混合。

Www.Svn8.Com

如果你的工作副本包含大量切换到不同资料库位置的子树,它继续像通常那样工作。当你更新时,你将接受到每个子树的适当的补丁。当你提交时,你的本地修改也将作为一个单独的原子的修改应用到资料库。

Www.Svn8.Com


注意,虽然你的工作副本反映资料库位置的混合没有问题,但这些位置必须都在相同的资料库内。 Subversion资料库还不能互相通信;这个特性超出了Subversion 1.0的计划。 [10] Svn8.Com

切换和更新
Www.Svn8.Com

你注意到svn switch和svn update的输出看起来是相同的了没有? switch命令其实是update命令的一个超集。 Www.Svn8.Com

当你运行svn update时,你在让资料库比较两个树。资料库做完后把对差异的描述发送回客户端。 svn switch 和 svn update 的唯一不同是update命令总是比较两个相同的路径。 Svn8.Com

就是说,如果你的工作副本是/calc/trunk的一个镜像,那么svn update 将自动比较你的/calc/trunk的工作副本和HEAD 修订版中的/calc/trunk。如果你把你要的工作副本切换到一个分支,那么svn switch 将比较你的/calc/trunk的工作副本和HEAD 修订版中的一些别的目录。

Www.Svn8.Com

换句话说,一次更新在时间轴上移动你的工作副本。一次切换在时间和空间上移动你的工作副本。 参考资料:www.svn8.com

因为svn switch本质上是svn update的一个变形,它们有相同的行为;当资料库中的新数据到来时,你工作副本中任何的本地修改都会被保留。这使你可以实行各种的聪明技巧。 Www.Svn8.Com

例如,假设你有个/calc/trunk的工作副本并对它作了大量的修改。那么你突然察觉到你应该在另一分支上作这些修改。没问题!当你 svn switch 你的工作副本到那个分支时,本地修改会保留。你可以接着测试并提交它们到那个分支。

Bbs.Svn8.Com


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

[10] 然而,如果你服务器的URL改变了而你不想丢弃现有的工作副本,你可以用svn switch 加上--relocate选项。参看???中关于svn switch这节获取更多信息和例子。

本文来自Svn中文网[www.svn8.com]转发请保留本站地址:http://www.svn8.com/svnsy/20080202/26.html

=========================

                                                                           标签
本文来自Svn中文网[www.svn8.com]转发请保留本站地址:
http://www.svn8.com/svnsy/20080202/27.html

标签版本控制的另一个共同的概念是标签(tag)。一个标签只是项目在时间上的“快照”。在Subversion中,这个概念已经好像无处不在了。每个资料库修订版实际就是一个标签——一次提交后文件系统的一个快照。 参考资料:www.svn8.com

然而,人们常想给标签以人更易识别的名字,比如release-1.0。并且他们想做文件系统里较小的子目录的快照。毕竟,要记住一个软件的release-1.0是修订版4822中一个特定的子目录不那么容易。 Www.Svn8.Com

创建简单的标签svn copy再次来拯救我们了。如果你想创建/calc/trunk 在HEAD修订版中样子的一个精确的快照,那么对它做一个复制: Svn8.Com

$ svn copy http://svn.example.com/repos/calc/trunk /
          
http://svn.example.com/repos/calc/tags/release-1.0 /
      -m "Tagging the 1.0 release of the 'calc' project."

Committed revision 351.
 Svn中文网 这个例子假设/calc/tags目录已经存在。(如果不是这样,参见???)。在复制完成后,新的release-1.0目录永远是你做复制的那个时间里项目在HEAD修订版中的样子。当然你可能想更精确的复制某个修订版。如果你知道/calc/trunk的修订版350就是你想要的,你可以通过传递-r 350给svn copy命令来指定它。

参考资料:www.svn8.com


等一下!创建标签的过程和我们用来创建分支的过程不一样吧?事实上,他们是一样的。在Subversion中,标签和分支没什么不同。这两者都是通过复制创建的普通的目录。正如分支一样,复制得来的目录是“标签”的唯一原因是因为人们决定这么看待它:只要没人提交到到这个目录,它永远是一个快照。如果人们开始对它提交,它就成了一个分支。 参考资料:www.svn8.com

如果你在管理一个资料库,有两种管理标签的办法。第一种是“放手管理”:作为一个项目政策,决定你的标签放在哪里,并确认所有的用户知道如何对待他们复制到那儿的目录。(就是说,确认他们知道不要对他们做提交。)第二个办法更偏执:你可以用随Subversion 一起提供的一个访问控制脚本来阻止任何人在标签区域做除了创建新拷贝外的任何事(参见第 6 章 服务器配置)。然而这个偏执的办法通常是不必要的。如果用户无意中在一个标签目录上提交了修改,你可以简单的撤销这些修改,像在前面的章节讨论的那样。毕竟这是版本控制。

Svn8.Com

创建一个复杂的标签有时你可能想让你的“快照”更复杂一点,不仅是单个修订版的单个目录。 参考资料:www.svn8.com

例如,假设你的项目比我们的例子calc要大得多:比如它包含大量的子目录和更多的文件。在我们工作过程中,你可能决定你需要创建一个这样的工作副本:它要包含指定的特性和bug修正。你可以通过把文件和目录有选择的回溯到特定的修订版(大量的使用svn update -r),或者通过把文件和目录切换到特定的分支(利用svn switch)来达到目的。当你完成后,你的工作副本是一个不同修订版的不同资料库位置组成的大杂烩。在测试后,你就知道它是你需要的数据的精确的组合。  Www.Svn8.Com

是时候来做一个快照了。把一个URL复制到另一个在这里行不通。这种情况下,你想对你的工作副本的布局的一个精确的快照并把它存到资料库。幸运的是,svn copy实际上有四种不同的用法(你在第九章能读到),包括复制一个工作副本树到资料库的能力。

Bbs.Svn8.Com

$ ls
my-working-copy/

$ svn copy my-working-copy http://svn.example.com/repos/calc/tags/mytag

Committed revision 352.
 Bbs.Svn8.Com 现在资料库里有了一个新目录,/calc/tags/mytag,它是你的工作副本一个精确的快照—— 混合修订版,URL以及全部。

Www.Svn8.Com


有些用户已经发现了这个特性的有趣的用法。有时有这种情况,你对你的工作副本已经作了很多修改,你想让一个合作者看到它们。你可以用svn copy来“上载” 你的工作副本到资料库的私有区域,而不是执行svn diff并发送一个补丁文件(它不会包含树修改)。然后你的合作者可以检出你的工作副本的一字不差的拷贝,或使用svn merge来接受你的精确的修改。 Bbs.Svn8.Com

本文来自Svn中文网[www.svn8.com]转发请保留本站地址:http://www.svn8.com/svnsy/20080202/27.html

===================================


转载于:http://www.svn8.com/svnsy/20080202/25.html

===========

 

===========================
                分支的维护
本文来自Svn中文网[www.svn8.com]转发请保留本站地址:
http://www.svn8.com/svnsy/20080202/28.html

分支的维护到现在你可能已经注意到Subversion是非常灵活的。因为它用相同的底层机制(目录拷贝)来实现分支和标签,并且因为分支和标签出现在普通的文件系统空间里,很多人觉得Subversion使人畏惧,它太灵活了。在这一节,我们将提出平时安排和管理你的数据的一些建议。

Svn中文网

资料库布局有一些标准的,推荐使用的组织资料库的方法。大部分人们创建一个trunk目录来保存开发的 “主线”,一个branches目录来包括分支拷贝,以及一个tags目录保存标签拷贝。如果资料库仅仅管理一个项目,那么通常人们创建这些顶级目录。

参考资料:www.svn8.com

/trunk
/branches
/tags
 Bbs.Svn8.Com 如果一个资料库包含多个项目,管理员通常用项目来索引它们的布局(参见“选择资料库布局”一节来读更多关于“项目根节点”的内容): Bbs.Svn8.Com

/paint/trunk
/paint/branches
/paint/tags
/calc/trunk
/calc/branches
/calc/tags
 Svn8.Com 当然你忽略这些常用布局也没关系。你可以创建任何变种,任何对你和你的团队最有效的布局。记住不管你选择了什么,它都不是一个永久的义务。你可以在任何时候重新组织你的资料库。因为分支和标签是普通的目录,svn move 命令可以随你所愿移动和重命名他们。从一种布局切换到另一种只涉及到一系列服务器端的移动;如果你不喜欢资料库中东西的组织方式,只要变换这些目录就行了。

参考资料:www.svn8.com

可是,记住虽然移动目录可以很容易,但是你也需要考虑你的用户。你的变动会迷惑那些已经有了工作副本的用户。如果一个用户有一个特定的资料库目录的工作副本,你的svn move操作可能从最新的修订版中删除了这个路径。当这个用户下次运行svn update,他被告知他们的工作副本表示了一个已经不存在的路径,用户将被迫用 svn switch切换到新的目录。 Svn8.Com

数据的生存时间Subversion模型的另一个很好的特性是分支和标签可以有有限的生存时间,就像任何别的版本化的项。例如,假设你彻底完成了calc项目中你个人的分支上的工作。在把你的所有修改合并回/calc/trunk后,你的私有分支目录不需要再待在哪儿了:

Svn8.Com

$ svn delete http://svn.example.com/repos/calc/branches/my-calc-branch /
             -m "Removing obsolete branch of calc project."

Committed revision 375.
 
Www.Svn8.Com 现在你的分支没了。当然它不是真的没有了:目录只是从HEAD修订版消失了,不再烦人。如果你对一个较早的修订版使用svn checkout, svn switch,或者 svn list,你将仍然能看到你的旧分支。

Bbs.Svn8.Com

如果光浏览你的删掉的目录还不够,那么你总能把它弄回来。在Subversion中恢复数据是非常容易的。如果有一个删掉的目录(或文件),你想把它弄回到HEAD,只要使用svn copy -r 来把它从老修订版里复制过来。

参考资料:www.svn8.com


$ svn copy -r 374 http://svn.example.com/repos/calc/branches/my-calc-branch /
                 
http://svn.example.com/repos/calc/branches/my-calc-branch

Committed revision 376.
 Bbs.Svn8.Com 在我们的例子里,你的个人分支有一个相对短的生存时间:你可能创建它是为了修正一个bug或实现一个新特性。当你的任务完成时,你的分支的使命也完成了。不过在软件开发中,有两个“主”分支并行的运行很长的一段时间也是很常见的。例如,假设到公开发布一个稳定的calc项目的时候了,并且你知道要花费几个月时间来除掉软件里的bug。你不想人们在这个项目上加新特性,但你也不想告诉所有的开发者停止编程。因此取而代之的是,你创建软件的一个“稳定”的分支,它不会修改太多: Svn中文网

$ svn copy http://svn.example.com/repos/calc/trunk /
        
http://svn.example.com/repos/calc/branches/stable-1.0 /
         -m "Creating stable branch of calc project."

Committed revision 377.
 Svn中文网 现在开发人员可以继续不受约束的在/calc/trunk上添加前沿的(或实验性的)特性,你可以宣布一个项目政策,只有bug修正能被提交到/calc/branches/stable-1.0。就是说,当人们继续在主线上工作时,人为挑选的一部分bug在稳定分支中被修正。甚至在稳定分支已经被发行后,在很长一段时间,你可能要继续维护这个分支——也就是说,只要你还在向客户提供对这个发行版的支持。

Bbs.Svn8.Com

本文来自Svn中文网[www.svn8.com]转发请保留本站地址:http://www.svn8.com/svnsy/20080202/28.html


==========================
 
                                   总结
                                  
                                  
在本章我们已经涉及了很大的范围。我们已经讨论了标签和分支的概念,并演示了Subversion怎么实现这些概念,它通过用svn copy复制目录来实现。我们演示了如何使用svn merge来把修改从一个分支复制到另一个,或者回滚坏的修改。我们已经谈了使用svn switch命令来创建混合位置工作副本。我们也已经谈到如何在资料库中管理布局和分支的生存时间。 Svn8.Com

记住Subversion的咒语:分支和标签是廉价的,因此自由的使用它们

本文来自Svn中文网[www.svn8.com]转发请保留本站地址:http://www.svn8.com/svnsy/20080202/29.html

 

本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/zgmzyr/archive/2011/01/14/6140607.aspx

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值