目 录
第1章 简介
SVN是Subversion的缩写,它是一款版本管理软件。所谓版本管理软件,首要的功能就是存储文件。此外,它还记录了这些文件的版本信息,用户可以查看各个版本之间的差异,也可以提取某一文件的任一个历史版本。
SVN采用C/S结构,即:客户/服务器结构。在客户端,使用的是TortoiseSVN;在服务器端,如果是Windows系统,可以使用VisualSVN。
第2章 SVN服务端
SVN服务端的配置有多种方案:可以安装Svnserve服务,也可以安装Apache服务,但最简单的莫过于使用VisualSVN Server这个程序。
2.1 安装VisualSVN
运行安装程序,如:VisualSVN-Server-2.1.10.msi。在Select Components(选择组件)界面下,请选择第一项"VisualSVN Server and Management Console"。单击"Next"按钮。
图2.1 选择VisualSVN安装组件
接下来的界面如下:
图2.2 VisualSVN安装选项
Location表示VisualSVN的安装目录;
Repositories表示版本库的位置。所有文件、代码、版本信息都将存在这里。
VisualSVN服务采用两种协议:http协议和https协议。后者是经过加密的,所以推荐使用它。勾中"Use secure connection(https://)"复选框就意味着使用https协议。
不管http协议还是https协议,都需要一个Server Port(服务端口)。除非这个端口已经被占用,否则请尽量采用默认值。
Authentication表示认证。服务器上版本库的内容不是任何人都可以访问的,需要客户端用户登录后才能访问。用户登录有两种方式:使用SVN认证或使用Windows认证。推荐使用第一项"Use Subversion authentication",即SVN认证。
单击"Next"按钮,再单击"Install"按钮,完成VisualSVN的安装。
2.2 VisualSVN服务
在Windows上安装VisualSVN之后,VisualSVN Server也就被安装并启动了。单击开始菜单的【运行】菜单项,输入services.msc后单击"确定"按钮。
图2.3 运行services.msc
Windows显示如下。可以看到VisualSVN Server已经被启动。启动类型为"自动",因此下次启动Windows后该服务会自动运行。因此,服务器无需运行VisualSVN即可实现客户端对服务器端版本库的访问。
图2.4 Windows服务
2.3 版本库
版本库就是存放文件、代码及版本信息的地方。服务器针对某一项目创建版本库后,客户端才能访问该项目的文件、代码。因此在服务器上创建、删除版本库是十分重要的工作。
2.3.1 创建版本库
运行VisualSVN,在其主界面下使用鼠标右键单击Repositories(版本库),在弹出菜单里单击【Create New Repository...】菜单项。
图2.5 创建版本库菜单
VisualSVN显示如下界面。请输入新的版本库名称后,单击"OK"按钮即可完成版本库的创建。注意"Create default structure(trunk,branches,tags)"这个复选框,勾中它之后再单击"OK"按钮,将在新建版本库里创建trunk、branches、tags这三个子目录。
图2.6 创建版本库界面
新建版本库后,VisualSVN主界面下将能看到这个新建的版本库,详见下图:
图2.7 新建的版本库
2.3.2 删除版本库
在图2.7中,鼠标右键单击Test版本库,然后在弹出菜单里单击【删除】按钮,即可完成Test版本库的删除。注意:删除版本库是无法撤销的,请谨慎处理。
2.4 用户和用户组
安装过程的图2.2中,提到了SVN认证。下面将介绍如何创建用户、用户组,并针对某个版本库给用户、用户组分配权限。这样客户端用户才能输入用户名、密码,完成SVN认证,进而访问自己需要的资料。
2.4.1 创建用户
假定现在某个软件项目有两组人员:开发组的D01、D02、D03;测试组的T01、T02、T03。首先增加用户D01。鼠标右键单击下图的"Users",在弹出菜单里单击【Create User...】菜单项。
图2.8 创建用户菜单
VisualSVN显示如下界面。请输入User name(用户名)为D01,再输入Password(密码)和Confirm password(确认密码),单击"OK"按钮即可完成用户D01的创建。
图2.9 创建用户界面
注意上图的"User name and password are case sensitive",它表示用户名和密码都是区分大小写的。
使用相同的方法增加开发组的D02、D03和测试组的T01、T02、T03。
2.4.2 创建用户组
下面将创建用户组Developer,并将用户D01、D02、D03加入该组。鼠标右键单击下图的"Groups",在弹出菜单里单击【Create Group...】菜单项。
图2.10 创建用户组菜单
VisualSVN显示如下图所示。请输入Group name(用户组名)为Developer,再单击"Add"按钮增加D01、D02、D03这三个用户。单击"OK"按钮完成用户组Developer的创建。
图2.11 创建用户组界面
使用相同方法可以创建测试组Tests,并将T01、T02、T03加入改组。
2.4.3 分配权限
针对Test版本库,需要设置开发组Developer和测试组Tests的权限。鼠标右键单击Test版本库,单击弹出菜单中的【Properties...】菜单项。
图2.12 设置权限菜单
VisualSVN显示如图2.13所示。首先选择Everyone,将其权限设置为No Access。其用意为:任何人都不能访问Test版本库。然后单击"Add..."按钮,增加Developer和Tests用户组。图2.13所示界面下,设置Developer的权限为Read/Write,设置Tests的权限为Read Only。这样开发组就可以读写Test版本库,而测试组只能读取Test版本库。
假定开发组的D03是个新手,不允许他进行写操作,则应该再次设置D03的权限。图2.13中,单击"Add..."按钮,增加D03用户。然后设置D03的权限为Read Only。
图2.13 设置权限界面
第3章 SVN客户端
SVN客户端为TortoiseSVN,安装之后就可以访问服务器端的版本库。读取项目文件、代码,还可以将修改后的文件、代码上传到服务器上,与项目组成员共同维护项目文件、代码。
3.1 安装TortoiseSVN
运行安装程序,如:TortoiseSVN-1.6.5.16974-win32-svn-1.6.5.msi。其安装步骤比较简单,这里就不赘述了。下文的讲解以该版本为准。
如果需要,可以安装TortoiseSVN的简体中文语言包,如:LanguagePack_1.6.5.16974-win32-zh_CN.msi。这样,使用TortoiseSVN时就可以灵活设置语言。
3.2 Import(导入)
导入、导出是以服务器上的版本库为中心的。导入就是将项目文档写入版本库。Windows资源管理器中,鼠标右键单击要导入的文件夹。在弹出菜单中,单击【TortoiseSVN】下的【Import...】菜单项。
图3.1 导入菜单
TortoiseSVN显示如下界面。在URL of repository下输入版本库的地址。在Import message下输入导入信息。单击"OK"按钮。
图3.2 导入设置界面
进入SVN认证界面。输入用户名和密码后,单击"OK"按钮完成认证。注意用户名和密码都是区分大小写的。如果觉得每次都要输入用户名和密码比较繁琐,请勾中"Save authentication"复选框。下次SVN会自动完成认证。
图3.3 认证界面
SVN完成认证后,会将选定的文件夹内容导入到版本库中。
需要说明的是
1、一般只是在项目开始的时候才会用到导入功能;
2、如果待导入的文件或文件夹在版本库内已经存在,则导入会失败。也就是说不能重复导入;
3、目录问题。一次只能导入一个文件夹。假如导入的文件夹为Folder。导入到版本库时不会有Folder这个文件夹;
3.3 Export(导出)
导出就是将项目文档从版本库中提取出来。Windows资源管理器中,单击鼠标右键。在弹出菜单中,单击【TortoiseSVN】下的【Export...】菜单项。SVN显示如下。
图3.4 导出界面
在URL of repository下输入版本库的地址。在Export directory下输入本机存放目录。导出深度有四个选项,分别为:
Fully recursive(深度递归):导出指定目录下的文件、各级子目录以及各级子目录下的文件。也就是全部导出。默认就是选择该项;
Immediate children, including folders(直接子节点,包含文件夹):导出指定目录下的文件和一级子目录,但不导出一级子目录内的文件;
Only file children(仅文件子节点):只导出指定目录下的文件,不导出一级子目录及一级子目录内的文件。
Only this item(仅此项):只导出指定目录。导出后就是一个空目录,没有什么实际意义。
Revision是版本的意思。项目组里任何一人修改了文档,上传到服务器上后都会形成一个新的版本,也就会有一个新的版本号。HEAD revision 表示最新的版本,也可以输入指定的版本号。
配置好后,单击"OK"按钮。
如果本机存放目录不为空,SVN会弹出如下信息。单击"Yes"继续导出。
图3.5 本级目录不为空
因为要和服务器通讯,因此与导入一样,需要进行用户认证。认证界面如图3.3所示。完成用户认证后,SVN会将版本库里的文档复制到本机。
3.4 Checkout(检出)
Windows资源管理器中,单击鼠标右键。在弹出菜单中,单击【SVN Checkout...】菜单项。剩下的步骤与导出大致相同。
3.4.1 两个版本
既然有导出功能了,还要检出做什么呢?因为与导出不同,检出的文档有两份。一份是用户能看到、修改的文档,被称之为Working Copy,以下简称Copy版;另一份是被隐藏在.SVN文件夹下的文档,被称之为Working BASE,以下简称BASE版。假如有Copy版的文件1.txt,则其对应的BASE版文件为.svn ext-base1.txt.svn-base。1.txt有两份,被称之为versioned文件。如果用户将2.txt复制到检出目录,则2.txt只有一份,被称之为non-versioned文件。
当用户修改了Copy版的文档,上传到服务器版本库的时候。SVN会比较Copy版和BASE版有什么不同之处,然后只将用户的修改信息上传至服务器。
当用户修改了Copy版的文档,该文档的图标会被改变。这也是SVN比较Copy版和BASE版的结果。
versioned文件有两个版本,因此对它的删除、重命名、移动必须通过SVN来完成,下面几节将介绍这些操作。
3.5 删除文件
资源管理器中,鼠标右键单击待删除的文件,单击弹出菜单的【TortoiseSVN】【Delete】菜单项即可完成删除操作。
图3.6 删除文件
3.6 重命名文件
资源管理器中,鼠标右键单击待重命名的文件,单击弹出菜单的【TortoiseSVN】【Rename...】菜单项。SVN显示如下界面。输入新的文件名后,单击"OK"按钮即可完成重命名操作。
图3.7 重命名文件
3.7 增加文件
检出目录里的non-versioned文件可以增加至BASE,变成versioned文件。
资源管理器中,选中若干项文件或文件夹。鼠标右键单击某一选中项。单击弹出菜单的【TortoiseSVN】【Add...】菜单项。SVN显示如下界面。单击"OK"按钮即可完成增加操作。
图3.8 增加文件
需要注意的是这个增加只是增加到本机的BASE,而不是直接增加到服务器的版本库中。
3.8 移动文件
资源管理器中,选中若干项文件或文件夹。移动鼠标至某一选中项,按下鼠标右键不放,移动鼠标至目标文件夹再放开鼠标右键。会弹出SVN右键菜单。下面两张图片一个是移动多个文件的,一个是移动单个文件的。
图3.9 移动多个文件
图3.10 移动单个文件
SVN Move……的含义是移动文件。SVN Copy……的含义是复制并增加文件。SVN……and rename 是移动或复制文件之后再重命名。
3.9 Commit(提交)
当用户对检出的文档做了修改后,需要将修改信息上传到服务器的版本库里,以便项目组的其它成员共享。这个上传的过程就是提交。
Windows资源管理器中,鼠标右键单击要提交的文件夹。在弹出菜单中,单击【SVN Commit...】菜单项。SVN显示如下界面:
图3.11 提交界面
message中写入你对项目文档都做了哪些修改。一定要养成填写该项的好习惯,方便项目组其他成员清楚项目都发生了哪些变化。
Changes made下的列表列出了哪些文件被改变了。改变的文件分为两大类:一类是在Copy版中存在,但在BASE版中不存在的文件,即non-versioned文件;另一类是在BASE版中存在的文件,即versioned文件,其Text status有可能是modified(修改)、added(增加)、deleted(删除)……
列表中勾中要提交的文件,单击"OK"按钮即可完成提交操作。提交过程中可能会产生冲突,其解决方法请参考"冲突"这一节。
3.9.1 non-versioned文件
对于non-versioned文件,SVN默认不提交至服务器。下次提交的时候,如果希望某个non-versioned文件不再出现在列表中,其方法就是忽略。
提交界面(图3.11)中,鼠标右键单击一个non-versioned文件夹,弹出菜单中有【Add to ignore list】。它有两个选项,一个是文件夹本身,另一个是*。前者表示忽略该文件夹,下次提交时该文件夹将不再显示在列表里。后者表示忽略所有non-versioned文件,下次提交时所有的non-versioned文件将不再显示在列表里。显然忽略*是应该谨慎使用的。
图3.12 忽略文件夹
提交界面(图3.11)中,鼠标右键单击一个non-versioned文件,弹出菜单中有【Add to ignore list】。以下图为例,它有两个选项,一个是文件本身,另一个是*.pch。前者表示忽略该文件,下次提交时该文件将不再显示在列表里。后者表示忽略所有扩展名为pch的non-versioned文件,下次提交时这些文件将不再显示在列表里。应该谨慎使用后者。
图3.13 忽略文件
忽略的文件信息被写入.SVN目录,具体请参考"Properties(属性)"。
VC++项目中,有些文件无需提交,如:*.aps、*.ncb、*.plg、*.clw……可以将这些文件忽略。此外,编译会产生大量的中间文件。可以将这些中间文件所在文件夹忽略。
3.9.2 versioned文件
对于versioned文件,SVN默认将其提交至服务器。如果下次提交的时候,不希望提交某个versioned文件,其方法就是将其移动到提交忽略列表。
提交界面(图3.11)中,鼠标右键单击一个versioned文件,单击弹出菜单中的【Move to changelist】【ignore-on-commit】菜单项即可将该文件移动到提交忽略列表。操作界面见图3.14。
移动到提交忽略列表中的文件,可以从该列表中移除。见图3.15,鼠标右键单击ignore-on-commit列表中的一个文件,单击弹出菜单中的【Remove from changelist】菜单项。
有些软件项目中会有配置文件。每运行一次软件,配置文件都有可能会被改动。这些文件需要上传至版本库,但每次修改后无需更新。此时,最好的办法就是将其移动到提交忽略列表。
图3.14 移动至提交忽略列表
图3.15 从列表中移除
3.10 Update(更新)
一个软件项目组有多个成员。成员A修改代码并提交之后,成员B如何获得最新版本?方法就是成员B需要更新本地代码。
Windows资源管理器中,鼠标右键单击检出的项目目录。在弹出菜单中,单击【SVN Update】菜单项。因为要和服务器通讯,因此需要进行用户认证。认证界面如图3.3所示。完成认证后,SVN将更新本地文件为最新版本。
注意:更新过程中可能会产生冲突,其解决方法请参考"冲突"这一节。
3.11 冲突
多人维护一套代码,以下情况是难免的:
1、程序员A和程序员B都修改了文件1.txt,然后都向版本库提交。版本库不知道以哪个版本为准,这会产生文件冲突;
2、程序员A修改了文件1.txt,向版本库提交时发现文件1.txt已经被程序员B删除了或重命名了或移动到别的目录了,这会产生树冲突。
3.11.1 文件冲突
文件冲突比较简单,就是多个成员对同一文件进行了修改,版本库不知道该以哪个版本为准。对于文件冲突,这里分两种情况说明。
3.11.1.1 文本文件
假定项目里有文件1.txt,其内容如下:
[开发组] 王二 [测试组] 李四 |
项目成员A负责维护开发组成员名单,项目成员B负责测试组成员名单。
现在开发组增加了"张三",测试组增加了"王五"。于是A修改1.txt为如下内容,并提交至版本库。
[开发组] 王二 张三 [测试组] 李四 |
B修改1.txt如下:
[开发组] 王二 [测试组] 李四 王五 |
当B提交的时候,就会出错。其出错界面如下:
图3.16 提交出错
上图说明1.txt已经被人修改,需要先更新。成员B按提示要求Update了本机代码,其界面如下。
图3.17 合并文件
请注意Merged这个单词,它表示SVN将版本库最新版本的1.txt和B修改的1.txt合并了。合并的结果如下,其内容正是A、B期望得到的结果。注意这个最新的文件只保留在B的本机上,需要B提交后版本库里的1.txt才能得到更新。
[开发组] 王二 张三 [测试组] 李四 王五 |
也就是说,对于文本文件,SVN有合并能力。它能将项目组成员的改动集中起来,减少了文件冲突的发生。但是SVN的合并不是万能的。假如B对1.txt做了非常大的改动,Update的时候将无法合并,并产生冲突,见下图
图3.18 更新时文件冲突
冲突发生的时候,资源管理器里1.txt有四个版本。其中三个见下图,另一个版本为.svn ext-base1.txt.svn-base,即更新后的BASE版。
图3.19 冲突文件有四个版本
这四个文件的区别请见下表。注意:如果在资源管理器里发现了1.txt.mine文件,则说明TortoiseSVN版本太低了,请升级至TortoiseSVN 1.6.5.16974或更高的版本。
表3.1 四个文件的区别
文 件 | 说 明 |
1.txt.r6 | BASE-OLD——更新前的BASE版,即更新前的1.txt.svn-base文件。版本号为6。 |
1.txt.r7 | HEAD——版本库里的最新版本,版本号为7。 r6和r7中版本号大的为HEAD版 |
1.txt | COPY——用户做的最新改动 |
.svn ext-base1.txt.svn-base | BASE-NEW——更新后的BASE版。与HEAD版完全相同。 |
既然发生了冲突,就需要解决它。成员B有三个方案:
1)完全信任自己,坚持自己所做的修改。其操作方法为:
在图3.18所示的界面下,鼠标右键单击1.txt,将弹出如下菜单。单击【Resolve conflict using 'mine'】或【Mark as resolved】菜单项即可。
图3.20 冲突右键菜单
如果图3.18所示的界面被关闭,请鼠标右键单击图3.19的1.txt文件,单击弹出菜单里的【TortoiseSVN】【Resolved...】菜单项。
这样操作实质上就是删除了1.txt.r6和1.txt.r7这两个文件。此外,修改了文件.svnentries,将里面的1.txt.r6和1.txt.r7删除了。
2)完全信任版本库,放弃自己所做的任何修改。其操作方法为:
在图3.18所示的界面下,鼠标右键单击1.txt,将弹出图3.20所示的菜单。单击【Resolve conflict using "theirs"】菜单项即可。
如果图3.18所示的界面被关闭,请鼠标右键单击图3.19的1.txt文件,单击弹出菜单里的【TortoiseSVN】【Revert...】菜单项。Revert的含义是还原,就是放弃一切修改。本质上就是用BASE版覆盖Copy版。
这样操作实质上就是使用HEAD版覆盖Copy版,然后又删除了1.txt.r6和1.txt.r7这两个文件。
3)折衷方案就是对1.txt进行合并修改。SVN默认使用TortoiseMerge合并。
在图3.18所示的界面下,鼠标右键单击1.txt,将弹出图3.20所示的菜单。单击【Edit conflicts】菜单项。
如果图3.18所示的界面被关闭,请鼠标右键单击图3.19的1.txt文件,单击弹出菜单里的【TortoiseSVN】【Edit conflicts】菜单项。
SVN将调用TortoiseMerge来进行合并编辑,其界面如下:
图3.21 TortoiseMerge程序界面
可以看到TortoiseMerge有三个窗格。Theirs表示HEAD版;Mine表示Copy版;Merged表示合并后的版本。此外,还有一个版本BASE-OLD。以上图为例:Theirs窗格中aaaaaaa就是BASE-OLD版的内容,ccc是HEAD版的内容。可以非常清楚的看到HEAD版相对BASE-OLD版做的改动。同样的Mine窗格同时显示BASE-OLD版和Copy版。
编辑好1.txt之后,单击【File】【Save】保存。再单击【Edit】【Mark as resolved】删除1.txt.r6和1.txt.r7。
也可以使用其它编辑器对1.txt进行编辑,然后单击【TortoiseSVN】【Resolved...】菜单项解决冲突。
3.11.1.2 二进制文件
一个项目里的文件并不都是文本文件,如:Word文档,bmp位图……遇到这类文件,SVN很难通过比较差异来替项目组成员解决冲突了。此时,最有效的方法是使用锁。
假定项目组成员A要编辑位图1.bmp,正确的操作步骤为:
1、请在资源管理器中右键单击该文件,单击弹出菜单里的【TortoiseSVN】【Get lock...】菜单项,SVN显示如下。请输入锁定该文件的原因,然后单击"OK"按钮完成文件锁定;
图3.22 锁定文件界面
2、如果A未对1.bmp做任何修改,暂时又不想编辑它,请尽快解锁。方法为右键单击该文件,单击弹出菜单里的【TortoiseSVN】【Release lock】菜单项;
3、如果A对1.bmp做了修改并确认无误后,请尽快提交它。方法为右键单击该文件,单击弹出菜单里的【SVN Commit...】菜单项。提交之后,SVN自动解锁该文件。
项目组成员B要编辑1.bmp同样遵循该步骤。因为一个文件不能同时被两个用户锁定,这样就保证了1.bmp不会发生冲突。
3.11.2 树冲突
发生树冲突的情况比较多,解决起来也比较复杂,应尽量避免。项目组成员需要遵循以下几个原则:
1、除非必要,否则不要删除、移动、重命名文件;
2、删除、移动、重命名文件提交时,一定要详细写明删除、移动、重命名了哪些文件,以及为什么这样做。这样其他项目组成员在Update的时候,可以根据这个说明照做一遍,解决树冲突。
3.12 Properties(属性)
前文提到了提交non-versioned文件时,可以忽略的操作。那么忽略的信息存放在哪儿了呢?答案就是文件夹属性。
鼠标右键单击检出代码的文件夹,单击弹出菜单的【TortoiseSVN】【Properties】菜单项。SVN显示如下。这里文件夹有一个很有趣的属性svn:ignore,它的值为*.pch,其含义为:提交时将忽略所有扩展名为pch的non-versioned文件。如果你想去除这个功能,就把这条属性编辑一下,或者直接删除即可。
图3.23 属性