SVN – 快速指南
SVN – 快速指南
SVN 基本概念
什么是版本控制系统?
版本控制系统(VCS) 是一种软件,可帮助软件开发人员协同工作并维护其工作的完整历史记录。
以下是版本控制系统的目标。
- 允许开发人员同时工作。
- 不要覆盖彼此的更改。
- 维护所有内容的每个版本的历史。
VCS 分为两类。
- 集中版本控制系统 (CVCS),以及
- 分布式/分散式版本控制系统 (DVCS)。
在本教程中,我们将只关注集中式版本控制系统,尤其是Subversion。Subversion 属于集中式版本控制系统,这意味着它使用中央服务器来存储所有文件并支持团队协作。
版本控制术语
让我们首先讨论我们将在本教程中使用的一些术语。
-
存储库:存储库是任何版本控制系统的核心。它是开发人员存储所有工作的中心位置。Repository 不仅存储文件,还存储历史。存储库通过网络访问,充当服务器,版本控制工具充当客户端。客户端可以连接到存储库,然后他们可以存储/检索对存储库的更改。通过存储更改,客户端将这些更改提供给其他人,并通过检索更改,客户端将其他人的更改作为工作副本。
-
主干:主干是所有主要开发发生的目录,通常由开发人员签出以进行项目。
-
标签:标签目录用于存储项目的命名快照。标签操作允许为存储库中的特定版本提供描述性和令人难忘的名称。
例如,LAST_STABLE_CODE_BEFORE_EMAIL_SUPPORT 比
存储库 UUID:7ceef8cb-3799-40dd-a067-c216ec2e5247 和
修订:13
-
分支:分支操作用于创建另一条开发线。当您希望开发过程分为两个不同的方向时,它很有用。例如,当您发布 5.0 版时,您可能想要创建一个分支,以便 6.0 功能的开发可以与 5.0 错误修复分开。
-
工作副本:工作副本是存储库的快照。存储库由所有团队共享,但人们不会直接修改它。相反,每个开发人员都会检查工作副本。工作副本是一个私人工作场所,开发人员可以在其中独立于团队的其他成员进行工作。
-
提交更改:提交是将更改从私人工作场所存储到中央服务器的过程。提交后,所有团队都可以使用更改。其他开发人员可以通过更新他们的工作副本来检索这些更改。提交是一个原子操作。要么整个提交成功,要么回滚。用户永远不会看到半完成的提交。
SVN环境设置
SVN安装
Subversion 是一种流行的开源版本控制工具。它是开源的,可通过互联网免费获得。大多数 GNU/Linux 发行版都默认提供它,因此它可能已经安装在您的系统上。要检查它是否已安装,请使用以下命令。
[jerry@CentOS ~]$ svn --version
如果没有安装 Subversion 客户端,则命令会报错,否则会显示已安装软件的版本。
[jerry@CentOS ~]$ svn --version -bash: svn: command not found
如果您使用的是基于 RPM 的 GNU/Linux,则使用yum命令进行安装。安装成功后,执行svn –version命令。
[jerry@CentOS ~]$ su - Password: [root@CentOS ~]# yum install subversion [jerry@CentOS ~]$ svn --version svn, version 1.6.11 (r934486) compiled Jun 23 2012, 00:44:03
如果您使用的是基于 Debian 的 GNU/Linux,则使用apt命令进行安装。
[jerry@Ubuntu]$ sudo apt-get update [sudo] password for jerry: [jerry@Ubuntu]$ sudo apt-get install subversion [jerry@Ubuntu]$ svn --version svn, version 1.7.5 (r1336830) compiled Jun 21 2013, 22:11:49
阿帕奇设置
我们已经看到了如何在 GNU/Linux 上安装 Subversion 客户端。让我们看看如何创建一个新的存储库并允许用户访问。
在服务器上,我们必须安装Apache httpd模块和svnadmin工具。
[jerry@CentOS ~]$ su - Password: [root@CentOS ~]# yum install mod_dav_svn subversion
所述mod_dav_svn的包允许访问使用HTTP的存储库,通过Apache HTTPD服务,颠覆软件包安装svnadmin的工具。
subversion 从/etc/httpd/conf.d/subversion.conf文件读取它的配置。添加配置后,subversion.conf文件如下所示:
LoadModule dav_svn_module modules/mod_dav_svn.so LoadModule authz_svn_module modules/mod_authz_svn.so <Location /svn> DAV svn SVNParentPath /var/www/svn AuthType Basic AuthName "Authorization Realm" AuthUserFile /etc/svn-users Require valid-user </Location>
让我们创建 Subversion 用户并授予他们对存储库的访问权限。htpasswd命令用于创建和更新用于存储用户名和密码的纯文本文件,用于HTTP 用户的基本身份验证。‘-c’ 选项创建密码文件,如果密码文件已经存在,它将被覆盖。这就是为什么只在第一次使用 ‘-c’ 选项的原因。‘-m’ 选项为密码启用 MD5 加密。
用户设置
让我们创建用户tom。
[root@CentOS ~]# htpasswd -cm /etc/svn-users tom New password: Re-type new password: Adding password for user tom
让我们创建用户jerry
[root@CentOS ~]# htpasswd -m /etc/svn-users jerry New password: Re-type new password: Adding password for user jerry [root@CentOS ~]#
创建 Subversion 父目录来存储所有工作(请参阅/etc/httpd/conf.d/subversion.conf)。
[root@CentOS ~]# mkdir /var/www/svn [root@CentOS ~]# cd /var/www/svn/
存储库设置
创建一个名为project_repo的项目存储库。svnadmin命令将创建一个新的存储库和其中的一些其他目录来存储元数据。
[root@CentOS svn]# svnadmin create project_repo [root@CentOS svn]# ls -l project_repo total 24 drwxr-xr-x. 2 root root 4096 Aug 4 22:30 conf drwxr-sr-x. 6 root root 4096 Aug 4 22:30 db -r--r--r--. 1 root root 2 Aug 4 22:30 format drwxr-xr-x. 2 root root 4096 Aug 4 22:30 hooks drwxr-xr-x. 2 root root 4096 Aug 4 22:30 locks -rw-r--r--. 1 root root 229 Aug 4 22:30 README.txt
让我们更改存储库的用户和组所有权。
[root@CentOS svn]# chown -R apache.apache project_repo/
使用 SELinux 状态工具检查SELinux是否启用。
[root@CentOS svn]# sestatus SELinux status: enabled SELinuxfs mount: /selinux Current mode: enforcing Mode from config file: enforcing Policy version: 24 Policy from config file: targeted
对于我们的服务器,SELinux 已启用,因此我们必须更改 SELinux 安全上下文。
[root@CentOS svn]# chcon -R -t httpd_sys_content_t /var/www/svn/project_repo/
要允许通过 HTTP 提交,请执行以下命令。
[root@CentOS svn]# chcon -R -t httpd_sys_rw_content_t /var/www/svn/project_repo/
重启Apache服务器,我们就完成了Apache服务器的配置。
[root@CentOS svn]# service httpd restart Stopping httpd: [FAILED] Starting httpd: httpd: apr_sockaddr_info_get() failed for CentOS httpd: Could not reliably determine the server's fully qualified domain name, using 127.0.0.1 for ServerName [ OK ] [root@CentOS svn]# service httpd status httpd (pid 1372) is running... [root@CentOS svn]#
我们已经成功配置了 Apache 服务器,现在我们将配置存储库。仅向真实用户提供存储库访问权限并使用默认授权文件;将以下行追加到project_repo/conf/svnserve.conf文件。
anon-access = none authz-db = authz
按照惯例,每个 Subversion 项目都直接在项目的根目录下有主干、标签和分支目录。
该干线是所有主要的发展情况,通常是签出由开发商来工作,对项目的目录。
该标签目录用于该项目的商店命名的快照。创建生产版本时,团队将标记进入该版本的代码。
该分支当你想追求发展的不同线路目录中。
让我们在项目存储库下创建主干、标签和分支目录结构。
[root@CentOS svn]# mkdir /tmp/svn-template [root@CentOS svn]# mkdir /tmp/svn-template/trunk [root@CentOS svn]# mkdir /tmp/svn-template/branches [root@CentOS svn]# mkdir /tmp/svn-template/tags
现在将目录从/tmp/svn-template导入存储库。
[root@CentOS svn]# svn import -m 'Create trunk, branches, tags directory structure' /tmp/svn-template/ Adding /tmp/svn-template/trunk Adding /tmp/svn-template/branches Adding /tmp/svn-template/tags Committed revision 1. [root@CentOS svn]#
这个完成了!我们已成功创建存储库并允许访问Tom and Jerry。从现在开始,他们可以对存储库执行所有支持的操作。
SVN 生命周期
本章讨论版本控制系统的生命周期。在后面的章节中,我们将看到每个操作的 Subversion 命令。
创建存储库:
存储库是开发人员存储所有工作的中心位置。Repository 不仅存储文件,还存储有关更改的历史记录。这意味着它维护文件中所做更改的历史记录。
“创建”操作用于创建新的存储库。大多数情况下,此操作仅执行一次。当您创建一个新的存储库时,您的 VCS 会希望您说出一些内容来标识它,例如您希望在何处创建它,或者应该为存储库指定什么名称。
查看
“Checkout”操作用于从存储库创建工作副本。工作副本是一个私人工作场所,开发人员可以在其中进行更改,然后将这些更改提交到存储库。
更新
顾名思义,’update’ 操作用于更新工作副本。此操作将工作副本与存储库同步。由于存储库由所有团队共享,其他开发人员可以提交他们的更改,并且您的工作副本会变旧。
让我们假设Tom和Jerry是从事一个项目的两个开发人员。两者都从存储库中检出最新版本并开始工作。此时,他们的工作副本与存储库完全同步。Jerry非常高效地完成了他的工作并将他的更改提交到存储库。
现在汤姆的工作副本已经过时了。更新操作将从存储库中提取Jerry 的最新更改,并将更新Tom 的工作副本。
执行更改
结帐后,可以执行各种操作来执行更改。编辑是最常见的操作。可以编辑现有文件以从文件中添加/删除内容。
可以添加文件/目录。但是这些文件/目录不会立即成为存储库的一部分,而是被添加到挂起的更改列表中,并在提交操作后成为存储库的一部分。
同样可以删除文件/目录。删除操作会立即从工作副本中删除文件,但实际删除的文件会添加到挂起的更改列表中,并在提交操作后对存储库进行更改。
“重命名”操作会更改文件/目录的名称。“移动”操作用于将文件/目录从存储库树中的一个位置移动到另一个位置。
查看更改
当您签出工作副本或更新工作副本时,您的工作副本将与存储库完全同步。但是当您对工作副本进行更改时,它会变得比存储库更新。在“提交”操作之前检查您的更改是一个很好的做法。
“状态”操作列出了对工作副本所做的修改。正如我们之前提到的,每当您在工作副本中进行更改时,所有这些更改都会成为待处理更改列表的一部分。“状态”操作用于查看挂起的更改列表。
“状态”操作仅提供更改列表,但不提供有关更改的详细信息。可以使用diff操作来查看对工作副本所做修改的详细信息。
修正错误
让我们假设有人对他的工作副本进行了更改,但是现在,他想丢弃这些更改。在这种情况下,“还原”操作会有所帮助。
还原操作还原对工作副本所做的修改。可以还原一个或多个文件/目录。还可以还原整个工作副本。在这种情况下,’revert’ 操作将销毁挂起的更改列表并将工作副本带回其原始状态。
解决冲突:
合并时可能会发生冲突。“合并”操作会自动处理所有可以安全完成的操作。其他一切都被视为冲突。例如,“hello.c”文件在分支中被修改并在另一个分支中被删除。这种情况需要一个人来做出决定。“解决”操作用于帮助用户解决问题并告知 VCS 处理冲突的方法。
提交更改
“提交”操作用于将更改从工作副本应用到存储库。此操作修改存储库,其他开发人员可以通过更新他们的工作副本来查看这些更改。
在提交之前,必须将文件/目录添加到挂起的更改列表中。这是等待提交更改的地方。通过提交,我们通常会提供一条日志消息来解释某人进行更改的原因。此日志消息成为存储库历史记录的一部分。提交是一个原子操作,这意味着要么整个提交成功,要么回滚。用户永远不会看到半完成的提交。
SVN结帐流程
Subversion 提供了checkout命令来从存储库中检出工作副本。下面的命令将在当前工作目录中创建一个名为project_repo的新目录。不要担心存储库 URL,因为大多数情况下,它已经由具有适当访问权限的 subversion 管理员提供。
[tom@CentOS ~]$ svn checkout http://svn.server.com/svn/project_repo --username=tom
上述命令将产生以下结果。
A project_repo/trunk A project_repo/branches A project_repo/tags Checked out revision 1.
每次成功签出操作后,都会打印修订号。如果要查看有关存储库的更多信息,请执行info命令。
[tom@CentOS trunk]$ pwd /home/tom/project_repo/trunk [tom@CentOS trunk]$ svn info
上述命令将产生以下结果。
Path: . URL: http://svn.server.com/svn/project_repo/trunk Repository Root: http://svn.server.com/svn/project_repo Repository UUID: 7ceef8cb-3799-40dd-a067-c216ec2e5247 Revision: 1 Node Kind: directory Schedule: normal Last Changed Author: jerry Last Changed Rev: 0 Last Changed Date: 2013-08-24 18:15:52 +0530 (Sat, 24 Aug 2013) [tom@CentOS trunk]$
SVN 执行更改
Jerry检出存储库的最新版本并开始处理一个项目。他在主干目录中创建了array.c文件。
[jerry@CentOS ~]$ cd project_repo/trunk/ [jerry@CentOS trunk]$ cat array.c
上述命令将产生以下结果。
#include <stdio.h> #define MAX 16 int main(void) { int i, n, arr[MAX]; printf("Enter the total number of elements: "); scanf("%d", &n); printf("Enter the elements\n"); for (i = 0; i < n; ++i) scanf("%d", &arr[i]); printf("Array has following elements\n"); for (i = 0; i < n; ++i) printf("|%d| ", arr[i]); printf("\n"); return 0; }
他想在提交之前测试他的代码。
[jerry@CentOS trunk]$ make array cc array.c -o array [jerry@CentOS trunk]$ ./array Enter the total number of elements: 5 Enter the elements 1 2 3 4 5 Array has following elements |1| |2| |3| |4| |5|
他编译并测试了他的代码,一切都按预期工作,现在是提交更改的时候了。
[jerry@CentOS trunk]$ svn status ? array.c ? array
Subversion 显示“?” 在文件名前面,因为它不知道如何处理这些文件。
在提交之前,Jerry需要将此文件添加到挂起的更改列表中。
[jerry@CentOS trunk]$ svn add array.c A array.c
让我们用“状态”操作来检查它。Subversion在array.c之前显示A,这意味着文件已成功添加到挂起的更改列表中。
[jerry@CentOS trunk]$ svn status ? array A array.c
要将array.c文件存储到存储库,请使用带有 -m 选项的 commit 命令,后跟提交消息。如果省略 -m 选项,Subversion 将打开文本编辑器,您可以在其中键入多行消息。
[jerry@CentOS trunk]$ svn commit -m "Initial commit" Adding trunk/array.c Transmitting file data . Committed revision 2.
现在array.c文件已成功添加到存储库中,并且修订号加一。
SVN 审核变更
Jerry已将array.c文件添加到存储库中。Tom还检查了最新的代码并开始工作。
[tom@CentOS ~]$ svn co http://svn.server.com/svn/project_repo --username=tom
以上命令将产生以下结果。
A project_repo/trunk A project_repo/trunk/array.c A project_repo/branches A project_repo/tags Checked out revision 2.
但是,他发现有人已经添加了代码。所以他很好奇是谁做的,他使用以下命令检查日志消息以查看更多详细信息:
[tom@CentOS trunk]$ svn log
以上命令将产生以下结果。
------------------------------------------------------------------------ r2 | jerry | 2013-08-17 20:40:43 +0530 (Sat, 17 Aug 2013) | 1 line Initial commit ------------------------------------------------------------------------ r1 | jerry | 2013-08-04 23:43:08 +0530 (Sun, 04 Aug 2013) | 1 line Create trunk, branches, tags directory structure ------------------------------------------------------------------------
当Tom观察Jerry 的代码时,他立即注意到其中的一个错误。Jerry 没有检查数组溢出,这可能会导致严重的问题。所以汤姆决定解决这个问题。修改后,array.c会是这个样子。
#include <stdio.h> #define MAX 16 int main(void) { int i, n, arr[MAX]; printf("Enter the total number of elements: "); scanf("%d", &n); /* handle array overflow condition */ if (n > MAX) { fprintf(stderr, "Number of elements must be less than %d\n", MAX); return 1; } printf("Enter the elements\n"); for (i = 0; i < n; ++i) scanf("%d", &arr[i]); printf("Array has following elements\n"); for (i = 0; i < n; ++i) printf("|%d| ", arr[i]); printf("\n"); return 0; }
Tom想使用 status 操作来查看挂起的更改列表。
[tom@CentOS trunk]$ svn status M array.c
array.c文件被修改,这就是为什么 Subversion在文件名前显示M字母。接下来,Tom编译并测试他的代码,它运行良好。在提交更改之前,他想通过审查他所做的更改来仔细检查它。
[tom@CentOS trunk]$ svn diff Index: array.c =================================================================== --- array.c (revision 2) +++ array.c (working copy) @@ -9,6 +9,11 @@ printf("Enter the total number of elements: "); scanf("%d", &n); + if (n > MAX) { + fprintf(stderr, "Number of elements must be less than %d\n", MAX); + return 1; + } + printf("Enter the elements\n"); for (i = 0; i < n; ++i)
Tom在array.c文件中添加了几行,这就是 Subversion在新行之前显示+符号的原因。现在他已准备好提交他的更改。
[tom@CentOS trunk]$ svn commit -m "Fix array overflow problem"
上述命令将产生以下结果。
Sending trunk/array.c Transmitting file data . Committed revision 3.
Tom 的更改已成功提交到存储库。
SVN更新流程
Jerry已经提交了代码的第一个版本。但他认为他应该编写两个函数来接受输入和显示数组内容。修改后,array.c如下所示。
#include <stdio.h> #define MAX 16 void accept_input(int *arr, int n) { int i; for (i = 0; i < n; ++i) scanf("%d", &arr[i]); } void display(int *arr, int n) { int i; for (i = 0; i < n; ++i) printf("|%d| ", arr[i]); printf("\n"); } int main(void) { int i, n, arr[MAX]; printf("Enter the total number of elements: "); scanf("%d", &n); printf("Enter the elements\n"); accept_input(arr, n); printf("Array has following elements\n"); display(arr, n); return 0; }
Jerry编译并测试他的代码并准备提交更改。在此之前,他想使用以下命令查看更改。
[jerry@CentOS trunk]$ svn diff
上述命令将产生以下结果。
Index: array.c =================================================================== --- array.c (revision 2) +++ array.c (working copy) @@ -2,6 +2,24 @@ #define MAX 16 +void accept_input(int *arr, int n) +{ + int i; + + for (i = 0; i & n; ++i) + scanf("%d", &arr[i]); +} + +void display(int *arr, int n) +{ + int i; + + for (i = 0; i < n; ++i) + printf("|%d| ", arr[i]); + + printf("\n"); +} + int main(void) { int i, n, arr[MAX]; @@ -10,15 +28,10 @@ scanf("%d", &n); printf("Enter the elements\n"); + accept_input(arr, n); - for (i = 0; i < n; ++i) - scanf("%d", &arr[i]); - printf("Array has following elements\n"); - for (i = 0; i < n; ++i) - printf("|%d| ", arr[i]); - - printf("\n"); + display(arr, n); return 0; }
对于新添加的行,Subversion在行前显示+号,对于删除的行,它显示–号。现在,Jerry 尝试使用以下命令提交更改:
[jerry@CentOS trunk]$ svn commit -m "Add function to accept input and to display array contents"
上述命令将产生以下结果。
Sending trunk/array.c svn: Commit failed (details follow): svn: File or directory 'array.c' is out of date; try updating svn: resource out of date; try updating
Subversion 不允许提交Jerry 的更改,因为Tom已经修改了存储库并且Jerry 的工作副本已过期。为了避免覆盖彼此的更改,Subversion 无法执行此操作。Jerry必须在提交更改之前更新工作副本。所以他使用如下所示的更新命令。
[jerry@CentOS trunk]$ svn update G array.c Updated to revision 3.
Subversion在文件名前显示字母G,这意味着该文件已被合并。
[jerry@CentOS trunk]$ svn diff
上述命令将产生以下结果。
Index: array.c =================================================================== --- array.c (revision 3) +++ array.c (working copy) @@ -2,6 +2,24 @@ #define MAX 16 +void accept_input(int *arr, int n) +{ + int i; + + for (i = 0; i < n; ++i) + scanf("%d", &arr[i]); +} + +void display(int *arr, int n) +{ + int i; + + for (i = 0; i < n; ++i) + printf("|%d| ", arr[i]); + + printf("\n"); +} + int main(void) { int i, n, arr[MAX]; @@ -15,15 +33,10 @@ } printf("Enter the elements\n"); + accept_input(arr, n); - for (i = 0; i < n; ++i) - scanf("%d", &arr[i]); - printf("Array has following elements\n"); - for (i = 0; i < n; ++i) - printf("|%d| ", arr[i]); - - printf("\n"); + display(arr, n); return 0; }
Subversion 仅显示Jerry 的更改,但array.c文件已合并。如果您仔细观察,Subversion 现在显示修订号 3。在之前的输出中,它显示修订号 2。只需查看谁对文件进行了更改以及出于什么目的。
jerry@CentOS trunk]$ svn log ------------------------------------------------------------------------ r3 | tom | 2013-08-18 20:21:50 +0530 (Sun, 18 Aug 2013) | 1 line Fix array overflow problem ------------------------------------------------------------------------ r2 | jerry | 2013-08-17 20:40:43 +0530 (Sat, 17 Aug 2013) | 1 line Initial commit ------------------------------------------------------------------------ r1 | jerry | 2013-08-04 23:43:08 +0530 (Sun, 04 Aug 2013) | 1 line Create trunk, branches, tags directory structure ------------------------------------------------------------------------
现在Jerry 的工作副本与存储库同步,他可以安全地提交他的更改。
[jerry@CentOS trunk]$ svn commit -m "Add function to accept input and to display array contents" Sending trunk/array.c Transmitting file data . Committed revision 4.
SVN 修复错误
假设Jerry不小心修改了array.c文件并且他得到了编译错误。现在他想扔掉这些变化。在这种情况下,“还原”操作会有所帮助。还原操作将撤消对文件或目录的任何本地更改并解决任何冲突状态。
[jerry@CentOS trunk]$ svn status
以上命令将产生以下结果。
M array.c
让我们尝试按如下方式制作数组:
[jerry@CentOS trunk]$ make array
以上命令将产生以下结果。
cc array.c -o array array.c: In function ‘main’: array.c:26: error: ‘n’ undeclared (first use in this function) array.c:26: error: (Each undeclared identifier is reported only once array.c:26: error: for each function it appears in.) array.c:34: error: ‘arr’ undeclared (first use in this function) make: *** [array] Error 1
Jerry对array.c文件执行“恢复”操作。
[jerry@CentOS trunk]$ svn revert array.c Reverted 'array.c' [jerry@CentOS trunk]$ svn status [jerry@CentOS trunk]$
现在编译代码。
[jerry@CentOS trunk]$ make array cc array.c -o array
恢复操作后,他的工作副本恢复到原来的状态。还原操作可以还原单个文件以及整个目录。要恢复目录,请使用 -R 选项,如下所示。
[jerry@CentOS project_repo]$ pwd /home/jerry/project_repo [jerry@CentOS project_repo]$ svn revert -R trunk
到目前为止,我们已经看到了如何恢复对工作副本所做的更改。但是如果你想恢复一个提交的修订怎么办!版本控制系统工具不允许从存储库中删除历史记录。我们只能追加历史。即使您从存储库中删除文件,它也会发生。要撤消旧修订,我们必须撤销旧修订中所做的任何更改,然后提交新修订。这称为反向合并。
让我们假设 Jerry 添加了用于线性搜索操作的代码。在验证之后,他提交了他的更改。
[jerry@CentOS trunk]$ svn diff Index: array.c =================================================================== --- array.c (revision 21) +++ array.c (working copy) @@ -2,6 +2,16 @@ #define MAX 16 +int linear_search(int *arr, int n, int key) +{ + int i; + + for (i = 0; i < n; ++i) + if (arr[i] == key) + return i; + return -1; +} + void bubble_sort(int *arr, int n) { int i, j, temp, flag = 1; [jerry@CentOS trunk]$ svn status ? array M array.c [jerry@CentOS trunk]$ svn commit -m "Added code for linear search" Sending trunk/array.c Transmitting file data . Committed revision 22.
杰瑞很好奇汤姆在做什么。所以他检查了 Subversion 日志消息。
[jerry@CentOS trunk]$ svn log
上述命令将产生以下结果。
------------------------------------------------------------------------ r5 | tom | 2013-08-24 17:15:28 +0530 (Sat, 24 Aug 2013) | 1 line Add binary search operation ------------------------------------------------------------------------ r4 | jerry | 2013-08-18 20:43:25 +0530 (Sun, 18 Aug 2013) | 1 line Add function to accept input and to display array contents
查看日志消息后,Jerry 意识到他犯了一个严重的错误。因为Tom已经实现了二分查找操作,比线性查找要好;他的代码是多余的,现在 Jerry 必须将他的更改恢复到以前的修订版。因此,首先找到存储库的当前版本。当前,存储库处于修订版 22,我们必须将其恢复到以前的修订版,即修订版 21。
[jerry@CentOS trunk]$ svn up At revision 22. [jerry@CentOS trunk]$ svn merge -r 22:21 array.c --- Reverse-merging r22 into 'array.c': U array.c [jerry@CentOS trunk]$ svn commit -m "Reverted to revision 21" Sending trunk/array.c Transmitting file data . Committed revision 23.
Tom决定为他们的项目添加一个 README 文件。所以他创建了自述文件并将待办事项列表添加到其中。添加此内容后,文件存储库为修订版 6。
[tom@CentOS trunk]$ cat README /* TODO: Add contents in README file */ [tom@CentOS trunk]$ svn status ? README [tom@CentOS trunk]$ svn add README A README [tom@CentOS trunk]$ svn commit -m "Added README file. Will update it's content in future." Adding trunk/README Transmitting file data . Committed revision 6.
Jerry检查了修订版 6 的最新代码。他立即开始工作。几个小时后,Tom更新 README 文件并提交他的更改。修改后的 README 将如下所示。
[tom@CentOS trunk]$ cat README * Supported operations: 1) Accept input 2) Display array elements [tom@CentOS trunk]$ svn status M README [tom@CentOS trunk]$ svn commit -m "Added supported operation in README" Sending trunk/README Transmitting file data . Committed revision 7.
现在,存储库是修订版 7,而Jerry 的工作副本已过时。Jerry还更新了 README 文件并尝试提交他的更改。
Jerry 的README 文件看起来像这样。
[jerry@CentOS trunk]$ cat README * File list 1) array.c Implementation of array operation. 2) README Instructions for user. [jerry@CentOS trunk]$ svn status M README [jerry@CentOS trunk]$ svn commit -m "Updated README" Sending trunk/README svn: Commit failed (details follow): svn: File or directory 'README' is out of date; try updating svn: resource out of date; try updating
步骤 1:查看冲突
Subversion 检测到自上次更新后自述文件已更改。因此,Jerry必须更新他的工作副本。
[jerry@CentOS trunk]$ svn up Conflict discovered in 'README'. Select: (p) postpone, (df) diff-full, (e) edit, (mc) mine-conflict, (tc) theirs-conflict, (s) show all options:
Subversion 抱怨 README 文件有冲突,Subversion 不知道如何解决。所以Jerry选择了df选项来查看冲突。
[jerry@CentOS trunk]$ svn up Conflict discovered in 'README'. Select: (p) postpone, (df) diff-full, (e) edit, (mc) mine-conflict, (tc) theirs-conflict, (s) show all options: df --- .svn/text-base/README.svn-base Sat Aug 24 18:07:13 2013 +++ .svn/tmp/README.tmp Sat Aug 24 18:13:03 2013 @@ -1 +1,11 @@ -/* TODO: Add contents in README file */ +<<<<<<< .mine +* File list + +1) array.c Implementation of array operation. +2) README Instructions for user. +======= +* Supported operations: + +1) Accept input +2) Display array elements +>>>>>>> .r7 Select: (p) postpone, (df) diff-full, (e) edit, (r) resolved, (mc) mine-conflict, (tc) theirs-conflict, (s) show all options:
第 2 步:推迟冲突
下一个Jerry选择延迟 (p)选项,以便他可以解决冲突。
Select: (p) postpone, (df) diff-full, (e) edit, (r) resolved, (mc) mine-conflict, (tc) theirs-conflict, (s) show all options: p C README Updated to revision 7. Summary of conflicts: Text conflicts: 1
在文本编辑器中打开 README 后,他意识到 Subversion 包含了Tom 的代码和他的带有冲突标记的代码。
[jerry@CentOS trunk]$ cat README <<<<<<< .min * File list 1) array.c Implementation of array operation. 2) README Instructions for user. ======= * Supported operations: 1) Accept input 2) Display array elements >>>>>>> .r7
Jerry想要Tom 的更改以及他的更改,因此他只是删除了包含冲突标记的行。
因此,修改后的 README 将如下所示。
[jerry@CentOS trunk]$ cat README * File list 1) array.c Implementation of array operation. 2) README Instructions for user. * Supported operations: 1) Accept input 2) Display array elements
Jerry解决了冲突,他重试提交。
[jerry@CentOS trunk]$ svn commit -m "Updated README" svn: Commit failed (details follow): svn: Aborting commit: '/home/jerry/project_repo/trunk/README' remains in conflict [jerry@CentOS trunk]$ svn status ? README.r6 ? README.r7 ? README.mine C README
第 3 步:解决冲突
在上面的 commit 中,字母C表示 README 文件中存在冲突。Jerry解决了冲突,但没有告诉 Subversion 他已经解决了冲突。他使用 resolve 命令通知 Subversion 有关冲突解决的信息。
[jerry@CentOS trunk]$ svn resolve --accept=working README Resolved conflicted state of 'README' [jerry@CentOS trunk]$ svn status M README [jerry@CentOS trunk]$ svn commit -m "Updated README" Sending trunk/README Transmitting file data . Committed revision 8.
SVN标签
版本控制系统通过使用可以为代码的特定版本赋予有意义的名称这一概念来支持标签操作。标签允许为特定版本的代码提供描述性和令人难忘的名称。例如BASIC_ARRAY_OPERATIONS比修订版 4更容易记住。
让我们用一个例子来看看标签操作。Tom 决定创建一个标签,以便他可以更轻松地访问代码。
[tom@CentOS project_repo]$ svn copy --revision=4 trunk/ tags/basic_array_operations
以上命令将产生以下结果。
A tags/basic_array_operations/array.c Updated to revision 4. A tags/basic_array_operations
成功完成后,将在标签目录中创建新目录。
[tom@CentOS project_repo]$ ls -l tags/ total 4 drwxrwxr-x. 3 tom tom 4096 Aug 24 18:18 basic_array_operations
Tom 想在提交之前仔细检查它。状态操作显示标记操作成功,因此他可以安全地提交更改。
[tom@CentOS project_repo]$ svn status A + tags/basic_array_operations [tom@CentOS project_repo]$ svn commit -m "Created tag for basic array operations" Adding tags/basic_array_operations Committed revision 5.
SVN 分支
分支操作创造了另一条发展线。当有人希望开发过程分为两个不同的方向时,它很有用。假设您发布了 1.0 版本的产品,您可能想要创建新分支,以便 2.0 的开发可以与 1.0 的错误修复分开。
在本节中,我们将看到如何创建、遍历和合并分支。Jerry因为冲突不高兴,所以他决定创建一个新的私人分支。
[jerry@CentOS project_repo]$ ls branches tags trunk [jerry@CentOS project_repo]$ svn copy trunk branches/jerry_branch A branches/jerry_branch [jerry@CentOS project_repo]$ svn status A + branches/jerry_branch [jerry@CentOS project_repo]$ svn commit -m "Jerry's private branch" Adding branches/jerry_branch Adding branches/jerry_branch/README Committed revision 9. [jerry@CentOS project_repo]$
现在杰瑞在他的私人分支机构工作。他为数组添加了排序操作。Jerry 修改后的代码如下所示。
[jerry@CentOS project_repo]$ cd branches/jerry_branch/ [jerry@CentOS jerry_branch]$ cat array.c
上述命令将产生以下结果。
#include <stdio.h> #define MAX 16 void bubble_sort(int *arr, int n) { int i, j, temp, flag = 1; for (i = 1; i < n && flag == 1; ++i) { flag = 0; for (j = 0; j < n - i; ++j) { if (arr[j] > arr[j + 1]) { flag = 1; temp = arr[j]; arr[j] = arr[j + 1]; arr[j + 1] = temp; } } } } void accept_input(int *arr, int n) { int i; for (i = 0; i < n; ++i) scanf("%d", &arr[i]); } void display(int *arr, int n) { int i; for (i = 0; i < n; ++i) printf("|%d| ", arr[i]); printf("\n"); } int main(void) { int i, n, key, ret, arr[MAX]; printf("Enter the total number of elements: "); scanf("%d", &n); /* Error handling for array overflow */ if (n >MAX) { fprintf(stderr, "Number of elements must be less than %d\n", MAX); return 1; } printf("Enter the elements\n"); accept_input(arr, n); printf("Array has following elements\n"); display(arr, n); printf("Sorted data is\n"); bubble_sort(arr, n); display(arr, n); return 0; }
Jerry 编译并测试他的代码并准备提交他的更改。
[jerry@CentOS jerry_branch]$ make array cc array.c -o array [jerry@CentOS jerry_branch]$ ./array
上述命令将产生以下结果。
Enter the total number of elements: 5 Enter the elements 10 -4 2 7 9 Array has following elements |10| |-4| |2| |7| |9| Sorted data is |-4| |2| |7| |9| |10| [jerry@CentOS jerry_branch]$ svn status ? array M array.c [jerry@CentOS jerry_branch]$ svn commit -m "Added sort operation" Sending jerry_branch/array.c Transmitting file data . Committed revision 10.
与此同时,在后备箱里,汤姆决定执行搜索操作。Tom 添加了搜索操作的代码,他的代码如下所示。
[tom@CentOS trunk]$ svn diff
上述命令将产生以下结果。
Index: array.c =================================================================== --- array.c (revision 10) +++ array.c (working copy) @@ -2,6 +2,27 @@ #define MAX 16 +int bin_search(int *arr, int n, int key) +{ + int low, high, mid; + + low = 0; + high = n - 1; + mid = low + (high - low) / 2; + + while (low <= high) { + if (arr[mid] == key) + return mid; + if (arr[mid] > key) + high = mid - 1; + else + low = mid + 1; + mid = low + (high - low) / 2; + } + + return -1; +} + void accept_input(int *arr, int n) { int i; @@ -22,7 +43,7 @@ int main(void) { - int i, n, arr[MAX]; + int i, n, ret, key, arr[MAX]; printf("Enter the total number of elements: "); scanf("%d", &n); @@ -39,5 +60,16 @@ printf("Array has following elements\n"); display(arr, n); + printf("Enter the element to be searched: "); + scanf("%d", &key); + + ret = bin_search(arr, n, key); + if (ret < 0) { + fprintf(stderr, "%d element not present in array\n", key); + return 1; + } + + printf("%d element found at location %d\n", key, ret + 1); + return 0; }
在审查之后,他提交了他的更改。
[tom@CentOS trunk]$ svn status ? array M array.c [tom@CentOS trunk]$ svn commit -m "Added search operation" Sending trunk/array.c Transmitting file data . Committed revision 11.
但是汤姆很好奇杰瑞在他的私人分支机构里做了什么。
[tom@CentOS trunk]$ cd ../branches/ [tom@CentOS branches]$ svn up A jerry_branch A jerry_branch/array.c A jerry_branch/README [tom@CentOS branches]$ svn log ------------------------------------------------------------------------ r9 | jerry | 2013-08-27 21:56:51 +0530 (Tue, 27 Aug 2013) | 1 line Added sort operation ------------------------------------------------------------------------
通过查看 Subversion 的日志消息,Tom 发现 Jerry 实现了“排序”操作。Tom 使用二分搜索算法实现搜索操作,它总是期望数据按排序顺序。但是如果用户以未排序的顺序提供数据呢?在这种情况下,二分查找操作将失败。所以他决定在搜索操作之前使用 Jerry 的代码对数据进行排序。因此他要求 Subversion 将 Jerry 分支中的代码合并到主干中。
[tom@CentOS trunk]$ pwd /home/tom/project_repo/trunk [tom@CentOS trunk]$ svn merge ../branches/jerry_branch/ --- Merging r9 through r11 into '.': U array.c
合并后,array.c 将如下所示。
[tom@CentOS trunk]$ cat array.c
上述命令将产生以下结果。
#include <stdio.h> #define MAX 16 void bubble_sort(int *arr, int n) { int i, j, temp, flag = 1; for (i = 1; i < n && flag == 1; ++i) { flag = 0; for (j = 0; j < n - i; ++j) { if (arr[j] > arr[j + 1]) { flag = 1; temp = arr[j]; arr[j] = arr[j + 1]; arr[j + 1] = temp; } } } } int bin_search(int *arr, int n, int key) { int low, high, mid; low = 0; high = n - 1; mid = low + (high - low) / 2; while (low <= high) { if (arr[mid] == key) return mid; if (arr[mid] > key) high = mid - 1; else low = mid + 1; mid = low + (high - low) / 2; } return -1; } void accept_input(int *arr, int n) { int i; for (i = 0; i < n; ++i) scanf("%d", &arr[i]); } void display(int *arr, int n) { int i; for (i = 0; i < n; ++i) printf("|%d| ", arr[i]); printf("\n"); } int main(void) { int i, n, ret, key, arr[MAX]; printf("Enter the total number of elements: "); scanf("%d", &n); /* Error handling for array overflow */ if (n > MAX) { fprintf(stderr, "Number of elements must be less than %d\n", MAX); return 1; } printf("Enter the elements\n"); accept_input(arr, n); printf("Array has following elements\n"); display(arr, n); printf("Sorted data is\n"); bubble_sort(arr, n); display(arr, n); printf("Enter the element to be searched: "); scanf("%d", &key); ret = bin_search(arr, n, key); if (ret < 0) { fprintf(stderr, "%d element not present in array\n", key); return 1; } printf("%d element found at location %d\n", key, ret + 1); return 0; }
在编译和测试之后,Tom 将他的更改提交到存储库。
[tom@CentOS trunk]$ make array cc array.c -o array [tom@CentOS trunk]$ ./array Enter the total number of elements: 5 Enter the elements 10 -2 8 15 3 Array has following elements |10| |-2| |8| |15| |3| Sorted data is |-2| |3| |8| |10| |15| Enter the element to be searched: -2 -2 element found at location 1 [tom@CentOS trunk]$ svn commit -m "Merge changes from Jerry's code" Sending trunk Sending trunk/array.c Transmitting file data . Committed revision 12. [tom@CentOS trunk]$