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]$ 
廣告
© . All rights reserved.