SVN 快速指南



SVN 基本概念

什么是版本控制系统?

版本控制系统 (VCS) 是一种软件,它帮助软件开发者一起工作并维护其工作的完整历史记录。

以下是版本控制系统的目标。

  • 允许开发者同时工作。
  • 避免互相覆盖更改。
  • 维护所有内容的每个版本的记录。

VCS 分为两类。

  • 集中式版本控制系统 (CVCS),以及
  • 分布式/去中心化版本控制系统 (DVCS)。

在本教程中,我们将只关注集中式版本控制系统,特别是Subversion。Subversion 属于集中式版本控制系统,这意味着它使用中央服务器存储所有文件并支持团队协作。

版本控制术语

让我们开始讨论一些在本教程中将使用的术语。

  • 版本库 (Repository): 版本库是任何版本控制系统的核心。它是开发者存储所有工作的中心位置。版本库不仅存储文件,还存储历史记录。版本库通过网络访问,充当服务器,版本控制工具充当客户端。客户端可以连接到版本库,然后可以将更改存储到版本库或从版本库检索更改。通过存储更改,客户端使这些更改对其他人可用;通过检索更改,客户端获取其他人的更改作为工作副本。

  • 主干 (Trunk): 主干是一个目录,所有主要开发都在这里进行,通常由开发者检出以处理项目。

  • 标签 (Tags): 标签目录用于存储项目的命名快照。标签操作允许为版本库中的特定版本赋予描述性且便于记忆的名称。

    例如,`LAST_STABLE_CODE_BEFORE_EMAIL_SUPPORT` 比

    版本库 UUID: 7ceef8cb-3799-40dd-a067-c216ec2e5247 和

    修订版本: 13

  • 分支 (Branches): 分支操作用于创建另一条开发线。当您希望开发过程分成两个不同的方向时,它非常有用。例如,当您发布 5.0 版本时,您可能希望创建一个分支,以便 6.0 功能的开发与 5.0 的错误修复分开。

  • 工作副本 (Working copy): 工作副本是版本库的快照。版本库由所有团队共享,但人们不会直接修改它。相反,每个开发者都会检出一个工作副本。工作副本是一个私有的工作区,开发者可以在其中进行工作,与团队的其他成员隔离。

  • 提交更改 (Commit changes): 提交是从私有工作区将更改存储到中央服务器的过程。提交后,更改将提供给整个团队。其他开发者可以通过更新他们的工作副本来检索这些更改。提交是一个原子操作。要么整个提交成功,要么回滚。用户永远不会看到未完成的提交。

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

Apache 设置

我们已经看到了如何在 GNU/Linux 上安装 Subversion 客户端。让我们看看如何创建一个新的版本库并允许用户访问。

在服务器上,我们必须安装Apache httpd模块和svnadmin工具。

[jerry@CentOS ~]$ su -
Password: 
[root@CentOS ~]# yum install mod_dav_svn subversion

mod_dav_svn包允许通过 Apache httpd 服务器使用 HTTP 访问版本库,而subversion包安装 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 项目在项目的根目录下直接包含trunk、tagsbranches目录。

trunk是所有主要开发都在这里进行的目录,通常由开发者检出以处理项目。

tags目录用于存储项目的命名快照。创建生产版本时,团队将标记进入版本的代码。

当您想要执行不同的开发线路时,可以使用branches目录。

让我们在项目版本库下创建trunk、tagsbranches目录结构。

[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]#

完成了!我们已成功创建版本库并允许TomJerry访问。从现在开始,他们可以对版本库执行所有受支持的操作。

SVN 生命周期

本章讨论版本控制系统的生命周期。在后面的章节中,我们将看到每个操作的 Subversion 命令。

创建版本库

版本库是开发者存储所有工作的中心位置。版本库不仅存储文件,还存储有关更改的历史记录。这意味着它维护了对文件所做更改的历史记录。

“创建”操作用于创建新的版本库。大多数情况下,此操作仅执行一次。当您创建一个新的版本库时,您的 VCS 将期望您说明一些内容来标识它,例如您希望将其创建的位置或应为版本库指定的名称。

检出

“检出”操作用于从版本库创建工作副本。工作副本是开发者进行更改的私有工作区,稍后将这些更改提交到版本库。

更新

顾名思义,“更新”操作用于更新工作副本。此操作将工作副本与版本库同步。由于版本库由所有团队共享,其他开发者可以提交他们的更改,而您的工作副本变得过时。

让我们假设TomJerry是两个从事同一个项目的开发者。两者都从版本库检出最新版本并开始工作。此时,他们的工作副本与版本库完全同步。Jerry非常高效地完成了他的工作并将更改提交到版本库。

现在Tom的工作副本已过期。“更新”操作将从版本库提取Jerry的最新更改,并更新Tom的工作副本。

执行更改

检出后,可以执行各种操作来执行更改。编辑是最常见的操作。可以编辑现有文件以向文件添加/删除内容。

可以添加文件/目录。但是,这些文件/目录不会立即成为版本库的一部分,而是添加到待处理更改列表中,并在提交操作后成为版本库的一部分。

同样,可以删除文件/目录。删除操作立即从工作副本中删除文件,但文件的实际删除将添加到待处理更改列表中,并在提交操作后对版本库进行更改。

“重命名”操作更改文件/目录的名称。“移动”操作用于将文件/目录从版本库树中的一个位置移动到另一个位置。

审查更改

检出工作副本或更新工作副本时,您的工作副本将与版本库完全同步。但是,当您对工作副本进行更改时,它将比版本库更新。在“提交”操作之前审查您的更改是一个好习惯。

“状态”操作列出已对工作副本进行的修改。如前所述,每当您在工作副本中进行更改时,所有这些更改都将成为待处理更改列表的一部分。“状态”操作用于查看待处理更改列表。

“状态”操作仅提供更改列表,而不提供有关它们的详细信息。可以使用diff操作查看已对工作副本进行的修改的详细信息。

修复错误

让我们假设有人已对他的工作副本进行了更改,但是现在他想要丢弃这些更改。在这种情况下,“回滚”操作将有所帮助。

回滚操作将回滚已对工作副本进行的修改。可以回滚一个或多个文件/目录。还可以回滚整个工作副本。在这种情况下,“回滚”操作将销毁待处理更改列表,并将工作副本恢复到其原始状态。

解决冲突

冲突可能在合并时发生。“合并”操作自动处理所有可以安全执行的操作。其他所有内容都被视为冲突。例如,"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 检出存储库的最新版本,并开始处理一个项目。他在 trunk 目录内创建了 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

让我们用 'status' 操作检查一下。Subversion 在 array.c 前面显示 A,这意味着该文件已成功添加到待处理的变更列表中。

[jerry@CentOS trunk]$ svn status
?       array
A       array.c

要将 array.c 文件存储到存储库,请使用 commit 命令,后跟 -m 选项以及提交消息。如果省略 -m 选项,Subversion 将调出文本编辑器,您可以在其中键入多行消息。

[jerry@CentOS trunk]$ svn commit -m "Initial commit"
Adding         trunk/array.c
Transmitting file data .
Committed revision 2.

现在 array.c 文件已成功添加到存储库中,修订号也增加了 1。

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 没有检查数组溢出,这可能会导致严重问题。因此,Tom 决定解决这个问题。修改后,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)

Tomarray.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 必须在提交更改之前更新工作副本。因此,他使用如下所示的 update 命令。

[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 文件,并且出现编译错误。现在他想要丢弃这些更改。在这种情况下,'revert' 操作将有所帮助。Revert 操作将撤消对文件或目录的任何本地更改,并解决任何冲突状态。

[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

Jerryarray.c 文件执行了 'revert' 操作。

[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

Revert 操作后,他的工作副本恢复到其原始状态。Revert 操作可以 revert 单个文件以及整个目录。要 revert 目录,请使用 -R 选项,如下所示。

[jerry@CentOS project_repo]$ pwd
/home/jerry/project_repo

[jerry@CentOS project_repo]$ svn revert -R trunk

到目前为止,我们已经了解了如何 revert 对工作副本所做的更改。但是,如果您想 revert 已提交的修订版本怎么办!版本控制系统工具不允许从存储库中删除历史记录。我们只能追加历史记录。即使您从存储库中删除文件,也会发生这种情况。要撤消旧修订版本,我们必须反转旧修订版本中所做的任何更改,然后提交新的修订版本。这称为反向合并。

让我们假设 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.

Jerry 好奇 Tom 正在做什么。因此,他检查了 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 必须将其更改 revert 到之前的修订版本。因此,首先找到存储库的当前修订版本。当前,存储库位于修订版本 22,我们必须将其 revert 到之前的修订版本,即修订版本 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 文件。因此,他创建了 README 文件并在其中添加了 TODO 列表。添加此文件后,文件存储库位于修订版本 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 检测到自上次更新以来 README 文件已更改。因此,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 选择 postpone(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:解决冲突

在上述提交中,字母 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标签

版本控制系统支持 tag 操作,使用此概念可以为代码的特定版本赋予有意义的名称。Tag 允许为代码的特定版本提供描述性和易于记忆的名称。例如,BASIC_ARRAY_OPERATIONSrevision 4 更容易记住。

让我们来看一个 tag 操作的示例。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

成功完成后,将在 tags 目录内创建新的目录。

[tom@CentOS project_repo]$ ls -l tags/
total 4
drwxrwxr-x. 3 tom tom 4096 Aug 24 18:18 basic_array_operations

Tom 想在提交之前仔细检查一下。Status 操作显示 tag 操作成功,因此他可以安全地提交他的更改。

[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分支

Branch 操作创建另一条开发线。当有人希望开发过程分成两个不同的方向时,这很有用。假设您已发布了 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 修改后的代码如下所示。

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

同时,在 trunk 中,Tom 决定实现搜索操作。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 好奇 Jerry 在他的私有分支中做了什么。

[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 分支中的代码合并到 trunk 中。

[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]$ 
广告